Symfony Circular Reference Exception для Doctrine onFlush Event Listener Service

Я создал службу для прослушивателя событий Doctrine onFlush. В этом слушателе я хотел бы ссылаться на общую функцию, которую я имею в другой службе, чтобы проверить путь ярлыка объекта. Эта другая служба использует диспетчер сущностей для этого, поэтому определение службы для этой другой службы внедряет администратор сущности доктрины в качестве аргумента конструктора. Но если я включу эту другую службу в свой основной прослушиватель событий onFlush, я получаю неприятную ошибку о круговых ссылках.

Я мог бы заставить эту службу entity_helper принять Entity Manager в функции 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 \ EventListener \ 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 (eg 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); } } } } } 

Основным решением этого (как я указывал) является удаление EntityManager или установщика из определения службы (и класса обслуживания). Вместо этого вы должны передать EntityManager в 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 \ EventListener \ 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 (eg 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); } } } } }