私は Doctrine ODM で遊んでいて、Mongo ドキュメントに i18n 対応のフィールドを作ろうとしています。これは私がMongoで達成したいことです:
{
"title": {
"en": "Car",
"eu": "Autoa"
}
}
Document に必要な 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", ...]
私は 2 つのアプローチを試みましたが、どちらも独自の落とし穴があります。私はそれらのどれも好きではありません。
カスタム注釈
ドキュメントとモンゴの間の仲介者となるクラスを作成しました。このクラスは、フィールド (この場合は $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()) { /* ... */ }
}
次に、Mongo 配列を Translation middleman オブジェクトに変換するカスタム FieldType を作成しました (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() { /* ... */ }
}
良い部分:
- 簡単な使い方: 1 つ
use
の 、プロパティ宣言、注釈、ゲッター。 - Mongo => Doctrine から OK を読み取ります。
- API 要件を満たしています。
悪い部分:
- DB に保存され
title
ません。これは、 Translation オブジェクトが親オブジェクトのプロパティを汚していないためだと思いますTranslated
。 - $title は、オブジェクトの作成時に仲介者の Translation オブジェクトに初期化されません。
- これは、コンストラクターでオブジェクトを初期化することで修正できますが、可能であれば、これを避けて使用法をできるだけ無駄にしないようにしたいと考えています。回避策を見つけなければなりません。
EmbedOne
2 番目の方法は、埋め込みドキュメントを使用する方法です。これは完全に機能しますが、独自の小さな問題があります。:-)
まず、埋め込みドキュメントの基本 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() { /* ... */ }
}
良い部分:
- それはうまく機能し、読み書きがうまくいきます。
- 簡単なセットアップ。
- 任意のデータ型で使用できるため
isTranslated
、新しい TranslationBoolean クラスを追加するだけで i18n ブール フィールドを簡単に追加できます。
悪い部分:
- 大きな問題ではありませんが、ロケールは Translation クラスにハードコードされています。配列を直接操作できると便利ですが、スキーマに別のレベルが追加され、型強制が失われる可能性があります。
- 他のアプローチと同様に、プロパティは初期化されませんが、コンストラクターで初期化するのと同じくらい簡単です (One2Many リレーションのように)。
結論
私は、現在のように機能する2番目のアプローチが好きです。両方のアプローチの悪い部分を克服する方法を知っていますか?
ありがとう!