【问题标题】:Circular Reference on preFlush关于 preFlush 的循环参考
【发布时间】:2015-10-22 13:34:42
【问题描述】:

我的依赖注入遇到了一些问题。我正在尝试在我的 preFlush 侦听器中将记录保存到数据库中。我通过我自己的服务(自定义日志服务)将此记录保存到数据库中。我已经尝试了几种方法,但都没有奏效,我已经尝试了我发现的每一个 google / stackover flow 结果,但恐怕还没有运气。

这是我的记录器类的设置,所有私有变量都已被省略但已设置(我使用请求堆栈和类中其他位置的翻译器,不确定我是否应该省略这个问题):

配置:

core.logger:
    class: xxx\CoreBundle\Logger
    arguments: [@request_stack, @doctrine.orm.entity_manager, @translator]

类:

public function __construct(RequestStack $requestStack, EntityManager $em, TranslatorInterface $t)
    {
        $this->requestStack = $requestStack;
        $this->em = $em
        $this->t = $t;
    }
public function addLogEntityChange($uow, $entity) {
        $changeset = $uow->getEntityChangeSet($entity);
        foreach($changeset as $key => $value) {
            $logEntity = new Log();
            //setStuff..
            $this->em->persist($logEntity);
        }
        $this->em->flush();
    }

这是我的接口代码

配置:

xxxcore.loggerlistener:
  class: xxx\CoreBundle\Listener\LoggerListener
  calls:
    - [ setLogger, [@core.logger] ]
  tags:
    - { name: doctrine.event_listener, event: preFlush }

界面:

    public function setLogger($logger)
    {
        $this->l = $logger;
    }
    public function preFlush(PreFlushEventArgs $args)
    {
        $em = $args->getEntityManager();
        $uow = $em->getUnitOfWork();
        $uow->computeChangeSets();

        foreach ($uow->getScheduledEntityUpdates() as $entity) {
            if($entity instanceof LoggerInterface) {
                $this->l->addLogEntityChange($uow, $entity);
            }

        }
    }

这导致了

Circular reference detected for service "doctrine.dbal.xxx_connection", path: "doctrine.dbal.xxx_connection".

我尝试在记录器类中手动设置实体管理器,因此删除了依赖注入并使用了 setEntityManager 函数,但这只是页面超时。我试过插入 service_container 但这也没有真正的帮助。

在我持久化实体后手动调用函数时,一切正常,但我想通过带有 preFlush 侦听器的接口自动执行此操作。

任何帮助/见解将不胜感激!

【问题讨论】:

  • 教义事件可能很棘手。我认为您的记录器调用 flush 的事实再次启动了 onFlush 并导致了循环。考虑制作第二个实体管理器仅用于日志记录。 symfony.com/doc/current/cookbook/doctrine/…。不确定这是否能解决问题。
  • 我的日志实体没有实现 LoggerInterface 所以它没有监听器,即使它有一个检查它不能通过 "if($entity instanceof LoggerInterface) ”。至少应该是这样的,对吧?
  • 不。事件由事件管理器触发,而不是由单个实体触发。每次调用 EntityManager::flush 时都会调用 LoggerListener::preFlush。这意味着每个可记录的实体都会一遍又一遍地被记录。因此需要一个 LoggerEntityManager。
  • 由于您是从 preFlush 事件中调用它,也许您不需要在您的记录器服务中调用刷新,刷新已经在处理中
  • Entity Log 没有实现 LoggerInterface,所以它不能(或者不应该)通过 preFlush 方法中的检查,所以没有递归。

标签: php symfony circular-reference


【解决方案1】:

有趣。这个以前出现过:Symfony Circular Reference Exception for Doctrine onFlush Event Listener Service

在学说标记过程中的某处必须有一个编译器传递,它将侦听器注入实体管理器并导致循环引用。

我检查了 DoctrineBundle 代码,是的,实体管理器依赖于它的监听器,当然如果一个监听器依赖于实体管理器,那么我们会得到一个循环引用。

我重复了这个问题:

## services.yml
cerad_project_level_logger:
  class: Cerad\ProjectLevel\LevelLogger
  arguments:
    # - '@doctrine.orm.entity_manager'

cerad_project_level_listener:
  class: Cerad\ProjectLevel\LevelListener
  arguments:
    - '@cerad_project_level_logger'
  tags:
    - { name: doctrine.event_listener, event: preFlush }

解决方法是在记录器方法中传递实体管理器,而不是注入它。

    foreach ($uow->getScheduledEntityUpdates() as $entity) {
        if($entity instanceof LoggerInterface) {
            $this->l->addLogEntityChange($em, $uow, $entity);
        }

我相当有信心您的代码仍然无法按预期工作,因为 preFlush 总是在 em->flush 之后调用,并且您最终会陷入循环,但至少这会克服循环引用错误。

如果你只做一个记录器实体管理器,所有这些问题都会消失。此外,您真的要登录到您的生产数据库吗?

【讨论】:

  • 我目前正在开发一个内部工具,所以是的,我想登录到“生产”数据库。我想我可以给记录器自己的实体管理器,因为我不喜欢每次我想记录一些东西时都必须将 EM 传递给记录器服务,因为该服务也可以在任何地方手动调用。感谢您超越并自己重现问题。明天我会玩,看看我能不能让它工作。
  • 这确实有效,我之前尝试过,但我确实陷入了那个循环,我只是不知道我在其中。我通过添加这样的额外检查解决了这个问题:if($entity instanceof LoggerInterface && !isset($entity->logged)) { $entity->logged = TRUE; ... 感谢您的所有帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-02-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-01
  • 1970-01-01
相关资源
最近更新 更多