【发布时间】:2013-06-15 07:29:27
【问题描述】:
我正在使用 Doctrine ODM,试图在我的 Mongo 文档中创建一些 i18n-able 字段。这就是我想在 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 中间人对象,反之亦然(convertTo* 方法似乎被 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、属性声明、注解和getter。 - 从 Mongo 读取 OK => Doctrine。
- 满足 API 要求。
不好的部分:
-
不保存到 DB,我想这是因为 Translation 对象没有弄脏父对象
Translated上的title属性。 - $title 在对象创建时未初始化为中间人 Translation 对象。
- 这可以通过在构造函数中初始化对象来解决,但如果可能的话,我想尽量避免这种情况,以保持使用尽可能精简。我得想办法解决。
EmbedOne
第二种方法包括使用嵌入式文档,这很有效,但有它自己的小问题。 :-)
首先,我的嵌入文档的基础翻译类,这个类将直接作用于类属性而不是数组属性:
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()) { /* ... */ }
}
然后,在我的项目中使用的翻译类,这将是实际的嵌入文档:
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() { /* ... */ }
}
好的部分:
- 它可以很好地工作、读写。
- 设置简单。
- 可用于任何数据类型,因此添加
isTranslatedi18n 布尔字段很容易,只需添加一个新的 TranslationBoolean 类。
不好的部分:
- 不是什么大问题,但语言环境在 Translation 类中是硬编码的,能够直接在数组上工作会很好,但这会在架构中添加另一个级别,并且类型强制可能会丢失。
- 与其他方法一样,属性不会被初始化,但在构造函数中初始化它很容易(就像任何 One2Many 关系一样)。
结论
我更喜欢第二种方法,它现在的工作方式。 您知道如何克服这两种方法的 BAD 部分吗?
谢谢!
【问题讨论】:
标签: mongodb doctrine-orm doctrine-odm