Doctrine 2 MongoDB ODM i18n

时间:2013-06-18 22:02:26

标签: mongodb doctrine-orm doctrine-odm

我正在使用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,属性声明,注释和吸气剂。
  • 从Mongo读取OK =>教义。
  • 符合API要求。

BAD部分:

  • 不保存到DB ,我认为这是因为Translation对象不会弄脏父对象title上的Translated属性。
  • $ title未在对象创建时初始化为中间人Translation对象。
    • 这可以通过在构造函数中初始化对象来解决,但如果可能的话,我想尽量避免这种情况,以尽可能保持精简。我必须找到一个解决方法。

EmbedOne

第二种方法包括使用嵌入式文档,这非常有效,但它有自己的小问题。 : - )

首先,我的嵌入式文档的基础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部分:

  • 不是一个大问题,但是语言环境在翻译类中是硬编码的,能够直接在数组上工作会很好但是这会在模式中添加另一个级别,类型强制可能会丢失。
  • 与其他方法一样,该属性不会被初始化,但在构造函数中初始化它很容易(就像任何One2Many关系一样)。

结论

我更喜欢第二种方法,它的工作原理如何。 您是否知道如何克服两种方法中的BAD部分?

谢谢!

1 个答案:

答案 0 :(得分:0)

关于类型方法:

    实际上没有使用
  • closureToMongo()
  • closureToPHP()HydratorFactory类使用。
  • convertToPHPValue()ClassMetadataInfo使用,但仅限于标识符。
  • convertToDatabaseValue()用于少数几个地方,例如UnitOfWorkPersistenceBuilder

缺少convertToDatabaseValue()实现可能与您看到TranslationType无法持久保存到数据库的原因有关。

可以通过在此字段中存储对象(Hash注释)来缓解“翻译类中的硬编码语言环境”问题,尽管这会向模型添加另一个图层。 EmbedMany对象数组包含每个语言代码和值的字段将占用更多空间,尽管模型可以更容易设计表单来编辑数据。

你有没有看过DoctrineExtensions图书馆?它包含一个用于具有可翻译文档/实体的组件(支持ODM和ORM)。 author已经放慢了自己的开发速度,但是项目中合并了大量的拉取请求并且被广泛使用。