我正在一个复杂的Symfony项目中工作,该项目将Doctrine ORM对象与Doctrine PHPCR-ODM文档混合使用。一切都很好,但我无法解决容器中监听器之间的循环依赖注入问题。
场景是这样的,我有多个ODM文档,在加载时设置ORM引用,这是通过事件监听器实现的。一个示例配置如下:
这将会失败,因为我们有循环依赖性:容器试图将ODM监听器注入到DocumentManager的监听器中,然后尝试将EntityManager注入其中,然后再次尝试注入其自身的监听器,每个监听器都试图注入DocumentManager,以此类推。(请注意,此示例使用Registry而非管理器,但结果相同)。
我尝试了几种不同的方法来解决这个问题,但还没有找到有效的方法。在单个项目中,有没有人能够使ORM和ODM之间的双向监听器像这样工作?
我发现周围很少有这方面的例子。到目前为止,我的解决办法是创建一个服务来处理这些对象的加载/持久化,然后通过该服务运行所有操作,但与使用优雅的事件驱动系统相比,它似乎非常hackish。
场景是这样的,我有多个ODM文档,在加载时设置ORM引用,这是通过事件监听器实现的。一个示例配置如下:
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
对象时,以上所有内容都可以完美运行。(这基本上是Doctrine文档中描述ORM和MongoDB ODM混合的精确实现)。
现在问题来了,当我想要在同一个应用程序中进行反向操作时,也就是说,我还想拥有一个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.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而非管理器,但结果相同)。
我尝试了几种不同的方法来解决这个问题,但还没有找到有效的方法。在单个项目中,有没有人能够使ORM和ODM之间的双向监听器像这样工作?
我发现周围很少有这方面的例子。到目前为止,我的解决办法是创建一个服务来处理这些对象的加载/持久化,然后通过该服务运行所有操作,但与使用优雅的事件驱动系统相比,它似乎非常hackish。