Doctrine ORM、Doctrine PHPCR-ODM、およびイベントリスナーによる円形の依存
-
21-12-2019 - |
質問
私はDoctrine ORMオブジェクトをDoctrine Phpcr-ODM文書とブレンドする複雑なsymfonyプロジェクトに取り組んでいます。すべてがうまく機能しますが、コンテナ内のリスナー間の円形の依存性注入の問題を解決できませんでした。
シナリオは、ロードされているときにORM参照を設定する複数のODM文書を持っています。これはイベントリスナーを介して行われます。構成例は次のとおりです。
services.yml
:
example.event_listener.my_document:
class: Example\Common\EventListener\MyDocumentEventListener
arguments: [@doctrine]
tags:
- { name: doctrine_phpcr.event_listener, event: postLoad }
- { name: doctrine_phpcr.event_listener, event: prePersist }
.
Example\Common\EventListener\MyDocumentEventListener.php
:
namespace Example\Common\EventListener;
use Example\Common\ODM\Document\MyDocument;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\ODM\PHPCR\DocumentManager;
/**
* Listener for {@link Example\Common\ODM\Document\MyDocument} events.
*/
class MyDocumentEventListener
{
/*
* @var Doctrine\Common\Persistence\ManagerRegistry
*/
private $managerRegistry;
/**
* Constructor.
*
* @param Doctrine\Common\Persistence\ManagerRegistry $documentManager A Doctrine {@link Doctrine\Common\Persistence\ManagerRegistry}.
*/
public function __construct(ManagerRegistry $managerRegistry)
{
$this->managerRegistry = $managerRegistry;
}
/**
* After loading a document, ensure that the references exist
* to each ORM dependency.
*
* @param Doctrine\Common\Persistence\Event\LifecycleEventArgs $args
*/
public function postLoad(LifecycleEventArgs $args)
{
if (get_class($args->getObject()) == 'Example\Common\ODM\Document\MyDocument') {
$this->loadDependencies($args->getObject(), $args->getObjectManager());
}
}
/**
* Prior to persisting a document, ensure that the references exist
* to each ORM dependency.
*
* @param Doctrine\Common\Persistence\Event\LifecycleEventArgs $args
*/
public function prePersist(LifecycleEventArgs $args)
{
if (get_class($args->getObject()) == 'Example\Common\ODM\Document\MyDocument') {
$this->loadDependencies($args->getObject(), $args->getObjectManager());
}
}
/**
* Pull relational information from the ORM database to populate
* those fields in the {@link Example\Common\ODM\Document\MyDocument} document that
* require it. Each field is populated as a reference, so it will be
* loaded from the database only if necessary.
*
* @param Example\Common\ODM\Document\MyDocument $document The MyDocument to load dependencies for.
* @param Doctrine\ODM\PHPCR\DocumentManager $documentManager The DocumentManager for the MyDocument.
*/
private function loadDependencies(MyDocument $document, DocumentManager $documentManager)
{
$reflectionClass = $documentManager->getClassMetadata(get_class($document))->getReflectionClass();
$exampleProperty = $reflectionClass->getProperty('example');
$exampleProperty->setAccessible(true);
$exampleProperty->setValue(
$document,
$this->managerRegistry->getManager()->getReference('Example\Common\ORM\Entity\MyEntity', $document->getExampleId())
);
}
}
.
MyDocument
オブジェクトを操作するときは、上記のすべてが完全に問題ありません。 (これは基本的に
今問題は、同じアプリケーション内で逆をしたい場合です。つまり、ODM文書への参照または参照を埋めるリスナーを持つORMエンティティを持ちたいです。< / P>
詳細コードを追加せずに、services.yml
設定を次のように拡張するとしましょう。
example.event_listener.my_document:
class: Example\Common\EventListener\MyDocumentEventListener
arguments: [@doctrine]
tags:
- { name: doctrine_phpcr.event_listener, event: postLoad }
- { name: doctrine_phpcr.event_listener, event: prePersist }
example.event_listener.my_entity:
class: Example\Common\EventListener\MyEntityEventListener
arguments: [@doctrine_phpcr]
tags:
- { name: doctrine.event_listener, event: prePersist }
- { name: doctrine.event_listener, event: postLoad }
.
これは循環依存関係があるため、コンテナはODMリスナーをDocumentManager
のリスナーに注入しようとします。これは、EntityManager
を挿入しようとします。これはそれぞれの試みのそれ自身のリスナーを挿入しようとします。 DocumentManager
などを注入するには。 (この例では、マネージャではなくRegistry
を使用していますが、結果は同じです)。
これを解決するためにいくつかの異なるアプローチを試してみましたが、まだ働くものに当たっていません。誰もが、1回のプロジェクトでこのように機能するために、ORMとODMの間の双方向リスナーを得ることができましたか?
私はこれを中心に、残念ながらいくつかの例を見つけました。こうして私の回避策は、これらのオブジェクトの読み込み/持続化を処理するためのサービスを作成してから、それを通してすべてを実行することですが、エレガントなイベント駆動システムを使用するのと比較して非常にハッキングされているようです。
解決
上記のコメントに記載されているように、正しい答えであることが判明したもので、この古い質問をクリーンアップします。通常はこれを避けますが、この特定の問題を解決する唯一の方法であることがわかりました。
誰かがORMベースの依存関係をロードするODMリスナーの例を探している場合は、ここに到着したものの作業例です。
サービス定義:
example.event_listener.odm.my_document:
class: Example\Common\EventListener\MyDocumentEventListener
arguments: [@service_container]
tags:
- { name: doctrine_phpcr.event_listener, event: postLoad }
.
リスナー:
<?php
namespace Example\Common\EventListener;
use Example\Common\Document\MyDocument;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use Doctrine\ODM\PHPCR\DocumentManager;
use Symfony\Component\DependencyInjection\Container;
class MyDocumentEventListener
{
/*
* @var \Symfony\Component\DependencyInjection\Container
*/
private $container;
/**
* Constructor.
*
* @param \Symfony\Component\DependencyInjection\Container $container A Symfony dependency injection container.
*/
public function __construct(Container $container)
{
$this->container = $container;
}
/**
* After loading a document, ensure that the references exist
* to each ORM dependency.
*
* @param \Doctrine\Common\Persistence\Event\LifecycleEventArgs $args
*/
public function postLoad(LifecycleEventArgs $args)
{
if (get_class($args->getObject()) == 'Example\Common\Document\MyDocument') {
$this->loadDependencies($args->getObject(), $args->getObjectManager());
}
}
/**
* Pull relational information from the ORM database to populate
* those fields in the document that require it. Each field is
* populated as a reference, so it will be loaded from the database only
* if necessary.
*
* @param \Example\Common\Document\MyDocument $document The document to load dependencies for.
* @param \Doctrine\ODM\PHPCR\DocumentManager $documentManager The DocumentManager for the document.
*/
private function loadDependencies(MyDocument $document, DocumentManager $documentManager)
{
$documentReflectionClass = $documentManager->getClassMetadata(get_class($document))->getReflectionClass();
$someOrmProperty = $documentReflectionClass->getProperty('orm_property');
$someOrmProperty->setAccessible(true);
$someOrmProperty->setValue(
$document,
$this->container->get('doctrine.orm.entity_manager')->getReference('Example\Common\Model\MyModel', $document->getOrmPropertyId())
);
}
}
.
これにより、文書クラスはIDをORMモデルエンティティに格納することができ、文書がロードされるたびに、文書内のORMモデルへの実際の参照を埋めます。これは、それがすべての財産についてすべてを知っているかのように振る舞い、かなりよく機能します。
この例ではPHPCR-ODMを使用していますが、MongoDB ODMも大丈夫です。