php - Doctrine onFlush 事件监听器服务的 Symfony 循环引用异常

标签 php symfony doctrine-orm

我已经为 Doctrine onFlush 事件监听器创建了一个服务。在这个监听器中,我想引用我在另一个 服务中的一个常用函数来检查实体的快捷路径。 other 服务使用实体管理器来执行此操作,因此该other 服务的服务定义将原则实体管理器作为构造函数参数注入(inject)。但是,如果我在我的主要 onFlush 事件监听器中包含该other 服务,我会收到有关循环引用的严重错误。

我可以让这个 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

服务.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\Service\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 (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 注入(inject)。相反,您必须将 EntityManager 传递到需要它的函数中。这可以防止循环引用。

我选择了这个而不是创建一个 setEntityManager 因为在调用函数之前必须在 EntityHelper 服务上设置它似乎很笨拙。直接将它传递给需要它的函数似乎更好。

更改如下:

服务.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\Service\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 (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);
                }

            }
        }
    }
}

关于php - Doctrine onFlush 事件监听器服务的 Symfony 循环引用异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25577801/

相关文章:

php - preg_match 有字符串大小限制

mongodb - 在 Doctrine on Symfony 中为 MongoDB 配置 ssl 连接

Symfony2 & Twig : display all fields and keys

php - 如何通过 FormBuilder 的字段传递 Twig 参数

php - 如何恢复根节点

Javascript 和 PHP 扫描裸体

php - 我想在我的代码中提交表单后保留帖子值(value)请帮忙(在输入框名称数量)

javascript - 删除上传的图像

Symfony2。使用数据 fixture 填充多对多连接表

php - 具有 varchar id 的 Doctrine2 实体不将 id 插入数据库