【问题标题】:Doctrine 2 MongoDB ODM i18n教义 2 MongoDB ODM i18n
【发布时间】: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() { /* ... */ }
}

好的部分:

  • 它可以很好地工作、读写。
  • 设置简单。
  • 可用于任何数据类型,因此添加 isTranslated i18n 布尔字段很容易,只需添加一个新的 TranslationBoolean 类。

不好的部分:

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

结论

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

谢谢!

【问题讨论】:

    标签: mongodb doctrine-orm doctrine-odm


    【解决方案1】:

    关于类型方法:

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

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-08
      • 2015-04-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多