我想知道如何让Eloquent为我自动从数据库中获取翻译内容,而无需我指定它。例如,不必像以下那样运行:
$article = Article::someTranslationMethod("en")->first(); //to get the English version
$article = Article::someTranslationMethod("de")->first(); //to get the German version
$article = Article::someTranslationMethod()->first(); //to get the current locale version
//etc
我希望能够简单地运行:
$article = Article::first();
并且已经翻译了结果 - 根据当前的区域设置或其他任何内容。
详细信息:
我的数据库架构由一个Articles表和一个Translations表组成。 (请原谅我的德语):
Articles
id | title | body
------------------------------------------------------------------------
1 | How to drink your coffee | Hi, this is an article about coffee.
Translations
id | language | remote_table | remote_id | remote_field | value
--------------------------------------------------------------------------------------------------------
1 | en | articles | 1 | title | How to drink your coffee
2 | de | articles | 1 | title | Wie sollte man seinen Kaffee trinken
3 | en | articles | 1 | body | Hi, this is an article about coffee.
4 | de | articles | 1 | body | Hallo, dieser Text geht um Kaffee.
因此,当语言环境设置为" en":$article->title
应该具有值"How to drink your coffee"
,但是当语言环境设置为" de": $article->title
的值应为"Wie sollte man seinen Kaffee trinken"
。
过去使用ActiveRecord,我只需在从数据库中提取结果后立即运行函数即可实现此目的。它将查看translations
表中的翻译,并将$article
对象的属性替换为其翻译。就是这样。每次我必须获取一篇文章时,我会运行$article = MyActiveRecord::FindFirst('articles');
之类的东西,我相信结果已经保留了翻译的值,而我不需要采取任何进一步的行动来实现这一点 - 这正是我在追求。
但是使用Laravel和Eloquent,这是不可能的。至少,没有开箱即用。 (即使它是,这将意味着改变Eloquent的源代码 - 这不是一个选项)。因此,必须手动实施。现在我可以想到三种可能的方法:
一种可能的方法是使用自定义基本模型扩展Eloquent \ Model类,然后让我的所有模型扩展自定义模型,并在该自定义模型中以某种方式添加此功能。问题是,我不知道如何(以及如果)我可以添加此功能。
另一种方法是使用Eloquent 关系。像$article->hasMany("translations")
这样的东西。在这种情况下的问题是,为了指定这样的关系,一个外键是不够的,因为translations
表保存数据库中每个其他表的翻译。因此,translations
表格中的文章不仅由其ID表示,还由remote_table
,remote_id
和remote_field
组合表示。即使我能以某种方式创建这种关系,我可能仍然需要采取进一步的行动 - 翻译的内容确实会包含在结果中,但$article->title
仍然会有英文值。例如。
我在搜索时发现的第三种方法是使用访问器和变更器。虽然我对它们没有经验,但我看不出它们如何解决这个问题,因为它不是数据格式化的问题,而是用另一个表中的数据替换它。此外,它们不会让我不必在获取数据后采取进一步行动。
我的下注是扩展Eloquent \ Model类,并以某种方式使它在那里发生。但我不知道如何。
任何想法都将受到高度赞赏。
答案 0 :(得分:1)
我认为您可以通过应用于所有Article
个查询的全局范围来实现此目的。
打开Article
模型,然后找到public static function boot()
(如果不存在,则创建它)
public static function boot()
{
parent::boot();
static::addGlobalScope('transliterated', function(Builder $builder){
return $builder->translate(app()->getLocale());
});
}
这应该是开箱即用的,并适用于针对Article::
答案 1 :(得分:0)
最终解决问题的解决方案是使用Eloquent 事件。我确实创建了一个扩展Eloquent \ Model的自定义基础模型类,我所做的是向它注册一个新的Eloquent事件,称为" loaded",它在模型实例创建后立即触发。所以现在可以相应地操作结果,并返回到我们已经翻译过的应用程序。
这是我的自定义基本模型,我正在注册自定义"已加载"事件:
<?php
//app/CustomBaseModel.php
namespace App;
use Illuminate\Database\Eloquent\Model;
abstract class CustomBaseModel extends Model
{
/**
* Register a loaded model event with the dispatcher.
*
* @param \Closure|string $callback
* @return void
*/
public static function loaded($callback)
{
static::registerModelEvent('loaded', $callback);
}
/**
* Get the observable event names.
*
* @return array
*/
public function getObservableEvents()
{
return array_merge(parent::getObservableEvents(), array('loaded'));
}
/**
* Create a new model instance that is existing.
*
* @param array $attributes
* @param $connection
* @return \Illuminate\Database\Eloquent\Model|static
*/
public function newFromBuilder($attributes = array(), $connection = NULL)
{
$instance = parent::newFromBuilder($attributes);
$instance->fireModelEvent('loaded', false);
return $instance;
}
}//end class
然后我必须告诉应用程序听取此事件,因此我为此创建了一个新的服务提供商:
<?php
//app/Providers/TranslationServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
class TranslationServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* @return void
*/
public function boot(DispatcherContract $events)
{
$events->listen('eloquent.loaded: *', function ($ourModelInstance) { // "*" is a wildcard matching all models, replace if you need to apply to only one model, for example "App\Article"
//translation code goes here
$ourModelInstance->translate(); //example
}, 0);
}
/**
* Register the application services.
*
* @return void
*/
public function register()
{
//
}
}
我在应用中注册了新的服务提供商:
<?php
//config/app.php
...
'providers' => [
...
/*
* Custom Service Providers...
*/
App\Providers\TranslationServiceProvider::class,
],
...
最后,对于我们希望能够使用&#34;加载&#34;的所有模型。 event with,我们从CustomBaseModel类扩展它们,而不是Eloquent \ Model 1:
<?php
//app/Article.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Article extends CustomBaseModel
{
//
}
瞧。每次我们加载文章时,&#34;加载&#34;事件被触发,因此我们可以随心所欲地使用它,然后将其返回给应用程序。
附注: