1

我正在玩 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 => Doctrine 中读取 OK。
  • 满足 API 要求。

不好的部分:

  • 不保存到 DB,我想这是因为 Translation 对象没有弄脏title父对象上的属性Translated
  • $title 不会在对象创建时初始化为中间人 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 类,这将是实际的嵌入文档:

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 关系一样)。

结论

我更喜欢第二种方法,它现在的工作方式。你知道如何克服这两种方法的坏部分吗?

谢谢!

4

1 回答 1

0

关于类型方法:

  • closureToMongo()实际上并没有使用。
  • closureToPHP()HydratorFactory班级使用。
  • convertToPHPValue()由 使用ClassMetadataInfo,但仅用于标识符。
  • convertToDatabaseValue()用于少数地方,例如UnitOfWorkPersistenceBuilder

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

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

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

于 2013-06-28T20:49:00.993 回答