[英]Symfony Circular Reference Exception for Doctrine onFlush Event Listener Service
我已經為Doctrine onFlush事件監聽器創建了一個服務。 在這個監聽器中,我想引用我在另一個服務中的常用函數來檢查實體的快捷方式路徑。 其他服務使用實體管理器來執行此操作,因此該其他服務的服務定義將doctrine實體管理器注入構造函數參數。 但是,如果我在我的主onFlush事件監聽器中包含其他服務,我會得到一個關於循環引用的令人討厭的錯誤。
我可以使這個entity_helper
服務在set函數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);
}
}
}
}
}
對此的基本解決方案(如我所指出的)是從服務定義(和服務類)中刪除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);
}
}
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.