我正在使用Doctrine ODM,尝试在我的Mongo文档中创建一些可用于i18n的字段。这就是我想在Mongo中实现的目标:
{
"title": {
"en": "Car",
"eu": "Autoa"
}
}
我想要的文档的PHP API将是这样的:
$doc->getTitle()->setDefaultLocale('en');
$doc->getTitle(); // "Car"
$doc->getTitle()->get('eu'); // "Autoa"
$doc->getTitle()->set('es', 'Coche');
$doc->getTitle()->setTranslations([
'fr' => 'Voiture',
'eu' => 'Kotxea',
]);
$doc->getTitle()->getTranslations(); // ["en" => "Car", ...]
我尝试了两种方法,它们都有自己的陷阱。我不喜欢他们。
我创建了一个类,它将成为文档和mongo之间的中间人。此类将放在字段中,在本例中为$ title。
class Translation
{
protected $default;
protected $translations;
public function __construct(array $translations = array()) { /* ... */ }
public function get($locale) { /* ... */ }
public function getTranslations() { /* ... */ }
public function set($locale, $value) { /* ... */ }
public function setDefaultLocale($default) { /* ... */ }
public function setTranslations(array $translations = array()) { /* ... */ }
}
然后,我创建了一个自定义FieldType,它将Mongo数组转换为Translation中间人对象而反之(transTo *方法似乎被Doctrine忽略,并且等于closureTo *方法,所以我将省略它们):
class TranslationType extends \Doctrine\ODM\MongoDB\Types\Type
{
public function convertToDatabaseValue($value) { /* ... */ }
public function convertToPHPValue($value) { /* ... */ }
public function closureToMongo()
{
return '$return = $value->getTranslations();';
}
public function closureToPHP()
{
return '$return = new \App\TransBundle\MongoDB\Translation($value);';
}
}
然后,我有我的注释:
/** @Annotation */
class Translation extends \Doctrine\ODM\MongoDB\Mapping\Annotations\AbstractField
{
public $type = 'translation';
}
文件:
use App\TransBundle\MongoDB\Annotations\Translation;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/** @MongoDB\Document */
class Translated
{
/** @MongoDB\Id */
protected $id;
/** @Translation */
protected $title;
public function getId() { /* ... */ }
public function getTitle() { /* ... */ }
}
好的部分:
use
,属性声明,注释和吸气剂。BAD部分:
title
上的Translated
属性。第二种方法包括使用嵌入式文档,这非常有效,但它有自己的小问题。 : - )
首先,我的嵌入式文档的基础Translation类,这个类将直接在类属性而不是数组属性上工作:
class BaseTranslation
{
public function __construct(array $translations = array()) { /* ... */ }
public function get($locale) { /* ... */ }
public function getTranslations() { /* ... */ }
public function set($locale, $value) { /* ... */ }
public function setDefaultLocale($default) { /* ... */ }
public function setTranslations(array $translations = array()) { /* ... */ }
}
然后,在我的项目中使用的Translation类,这将是实际的embbeded文档:
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/** @MongoDB\EmbeddedDocument */
class Translation extends BaseTranslation
{
/** @MongoDB\String */
protected $en;
/** @MongoDB\String */
protected $es;
/** @MongoDB\String */
protected $fr;
}
最后是文件
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/** @MongoDB\Document */
class Translated
{
/** @MongoDB\Id */
protected $id;
/** @MongoDB\EmbedOne(targetDocument="Translation") */
protected $title;
public function getId() { /* ... */ }
public function getTitle() { /* ... */ }
}
好的部分:
isTranslated
i18n布尔字段很容易,只需添加一个新的TranslationBoolean类。BAD部分:
我更喜欢第二种方法,它的工作原理如何。 您是否知道如何克服两种方法中的BAD部分?
谢谢!
答案 0 :(得分:0)
关于类型方法:
closureToMongo()
。closureToPHP()
由HydratorFactory
类使用。convertToPHPValue()
由ClassMetadataInfo
使用,但仅限于标识符。convertToDatabaseValue()
用于少数几个地方,例如UnitOfWork
和PersistenceBuilder
。缺少convertToDatabaseValue()
实现可能与您看到TranslationType
无法持久保存到数据库的原因有关。
可以通过在此字段中存储对象(Hash
注释)来缓解“翻译类中的硬编码语言环境”问题,尽管这会向模型添加另一个图层。 EmbedMany
对象数组包含每个语言代码和值的字段将占用更多空间,尽管模型可以更容易设计表单来编辑数据。
你有没有看过DoctrineExtensions图书馆?它包含一个用于具有可翻译文档/实体的组件(支持ODM和ORM)。 author已经放慢了自己的开发速度,但是项目中合并了大量的拉取请求并且被广泛使用。