我为Doctrine onFlush事件监听器创建了一个服务。在这个监听器中,我想引用我在另一个服务中的常用函数来检查实体的快捷方式路径。 其他服务使用实体管理器来执行此操作,因此该其他服务的服务定义将doctrine实体管理器作为构造函数参数注入。但是如果我在我的主onFlush事件监听器中包含其他服务,我会得到一个关于循环引用的讨厌错误。
我可以让这个entity_helper
服务接受设置函数setEntityManager($entityManager)
中的实体管理器。但这意味着每当我在其他地方使用此entity_helper
服务时,我必须始终传入EntityManager。也许那没关系,但这是唯一的解决方案吗?开始时我的逻辑/理解是否有问题? (我是Symfony的新手,所以我经常出错。)
图表A:讨厌的错误
Fatal error: Uncaught exception 'Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException' with message 'Circular reference detected for service "doctrine.dbal.cms_connection", path: "doctrine.dbal.cms_connection".' in /var/www/core/cms/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php:456 Stack trace: #0 /var/www/core/cms/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php(604): Symfony\Component\DependencyInjection\Dumper\PhpDumper->addServiceInlinedDefinitionsSetup('doctrine.dbal.c...', Object(Symfony\Component\DependencyInjection\Definition)) #1 /var/www/core/cms/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php(630): Symfony\Component\DependencyInjection\Dumper\PhpDumper->addService('doctrine.dbal.c...', Object(Symfony\Component\DependencyInjection\Definition)) #2 /var/www/core/cms/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php(117): Symfony\Componen in /var/www/core/cms/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php on line 456
Services.yml
# This is the helper class for all entities
gutensite_cms.entity_helper:
class: Gutensite\CmsBundle\Service\EntityHelper
# This causes the Circular Reference Error when this service is included in Event Listener
arguments: [ "@doctrine.orm.cms_entity_manager" ]
# Passing via a setter injection also causes the same error.
#calls:
# - [setEntityManager, ['@doctrine.orm.cms_entity_manager']]
# An event listener for any entity that is Versionable
gutensite_cms.listener.is_versionable:
class: Gutensite\CmsBundle\EventListener\IsVersionableListener
#only pass in the services we need
arguments: [ "@gutensite_cms.entity_helper" ]
tags:
- { name: doctrine.event_listener, event: onFlush }
Gutensite \ CmsBundle \服务\ EntityHelper
namespace Gutensite\CmsBundle\Service;
use Doctrine\ORM\EntityManager;
class EntityHelper {
/**
* @var $em EntityManager
*/
private $em;
public function __construct(EntityManager $entityManager) {
$this->em = $entityManager;
}
/**
* Get the bundle shortcut path for an entity based on it's namespace.
*
* As an example, if your entity is Gutensite\CmsBundle\Entity\View\ViewVersion the function will return
* GutensiteCmsBundle:View\ViewVersion
*
* @param $entity
* @return string
*/
public function getEntityBundleShortcut($entity) {
// wrap get_class() in the entityManager metadata function to avoid returning cached proxy class
$path = explode('\Entity\\', $this->em->getClassMetadata(get_class($entity))->getName());
return str_replace('\\', '', $path[0]).':'.$path[1];
}
}
Gutensite \ CmsBundle \事件监听\ IsVersionableListener
namespace Gutensite\CmsBundle\EventListener;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Gutensite\CmsBundle\Service\EntityHelper;
/**
* Class IsVersionableListener
* @package Gutensite\CmsBundle\EventListener
*/
class IsVersionableListener
{
/*
private $entityHelper;
public function __construct(EntityHelper $entityHelper) {
$this->entityHelper = $entityHelper;
}
*/
public function onFlush(OnFlushEventArgs $eventArgs)
{
// This never is excecuted because of the error
print('ON FLUSH EVENT EXECUTED');
exit;
$em = $eventArgs->getEntityManager();
$uow = $em->getUnitOfWork();
$updatedEntities = $uow->getScheduledEntityUpdates();
foreach($updatedEntities AS $entity) {
// This is generic listener for all entities that have an isVersionable method (e.g. ViewVersion)
// TODO: at the moment, we only want to do the following code for the viewVersion entity
if (method_exists($entity, 'isVersionable') && $entity->isVersionable()) {
// Get the Correct Repo for this entity
$entityShortcut = $this->entityHelper->getEntityBundleShortcut($entity);
$repo = $em->getRepository($entityShortcut);
// If the repo for this entity has an onFlush method, use it.
// This allows us to keep the functionality in the entity repo
if(method_exists($repo, 'onFlush')) {
$repo->onFlush($em, $entity);
}
}
}
}
}
答案 0 :(得分:4)
对此的基本解决方案(正如我所指出的)是从服务定义(和服务类)中删除EntityManager
的构造或setter注入。相反,您必须将EntityManager
传递给需要它的函数。这会阻止循环引用。
我选择了这个而不是创建setEntityManager
,因为在调用函数之前必须在EntityHelper服务上设置它似乎很笨拙。将它直接传递给需要它的函数似乎更好。
Services.yml
# This is the helper class for all entities
gutensite_cms.entity_helper:
class: Gutensite\CmsBundle\Service\EntityHelper
# Do NOT pass in EntityManager via constructor or injector, because it causes a Circular Reference Error when this service is included in Event Listener
# An event listener for any entity that is Versionable
gutensite_cms.listener.is_versionable:
class: Gutensite\CmsBundle\EventListener\IsVersionableListener
#only pass in the services we need
arguments: [ "@gutensite_cms.entity_helper" ]
tags:
- { name: doctrine.event_listener, event: onFlush }
Gutensite \ CmsBundle \服务\ EntityHelper
namespace Gutensite\CmsBundle\Service;
use Doctrine\ORM\EntityManager;
class EntityHelper {
/**
* Get the bundle shortcut path for an entity based on it's namespace.
*
* As an example, if your entity is Gutensite\CmsBundle\Entity\View\ViewVersion the function will return
* GutensiteCmsBundle:View\ViewVersion
*
* @param $entity
* @return string
*/
public function getEntityBundleShortcut(EventManager $eventManager, $entity) {
// wrap get_class() in the entityManager metadata function to avoid returning cached proxy class
$path = explode('\Entity\\', $eventManager->getClassMetadata(get_class($entity))->getName());
return str_replace('\\', '', $path[0]).':'.$path[1];
}
}
Gutensite \ CmsBundle \事件监听\ IsVersionableListener
namespace Gutensite\CmsBundle\EventListener;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Gutensite\CmsBundle\Service\EntityHelper;
/**
* Class IsVersionableListener
* @package Gutensite\CmsBundle\EventListener
*/
class IsVersionableListener
{
/*
private $entityHelper;
public function __construct(EntityHelper $entityHelper) {
$this->entityHelper = $entityHelper;
}
*/
public function onFlush(OnFlushEventArgs $eventArgs)
{
$em = $eventArgs->getEntityManager();
$uow = $em->getUnitOfWork();
$updatedEntities = $uow->getScheduledEntityUpdates();
foreach($updatedEntities AS $entity) {
// This is generic listener for all entities that have an isVersionable method (e.g. ViewVersion)
// TODO: at the moment, we only want to do the following code for the viewVersion entity
if (method_exists($entity, 'isVersionable') && $entity->isVersionable()) {
// Get the Correct Repo for this entity
$entityShortcut = $this->entityHelper->getEntityBundleShortcut($em, $entity);
$repo = $em->getRepository($entityShortcut);
// If the repo for this entity has an onFlush method, use it.
// This allows us to keep the functionality in the entity repo
if(method_exists($repo, 'onFlush')) {
$repo->onFlush($em, $entity);
}
}
}
}
}