在postPersist事件中插入主义

nuc*_*tux 16 symfony doctrine-orm symfony-sonata

我想在实体持久化和更新上添加新的Feed项.我写这个事件监听器(postUpdate是一样的):

public function postPersist(LifecycleEventArgs $args)
{
    $entity = $args->getEntity();
    $em = $args->getEntityManager();

    if ($entity instanceof FeedItemInterface) {
        $feed = new FeedEntity();
        $feed->setTitle($entity->getFeedTitle());
        $feed->setEntity($entity->getFeedEntityId());
        $feed->setType($entity->getFeedType());
        if($entity->isFeedTranslatable()) {
            $feed->getEnTranslation()->setTitle($entity->getFeedTitle('en'));
        }
        $em->persist($feed);
        $em->flush();
    }
}
Run Code Online (Sandbox Code Playgroud)

但是我得到了

完整性约束违规:1062重复条目'30 -2'用于键'PRIMARY'

在日志中有两个插入:

INSERT INTO interview_scientificdirection(interview_id,scientificdirection_id)VALUES(?,?)([30,2])INSERT INTO interview_scientificdirection(interview_id,scientificdirection_id)VALUES(?,?)([30,2])

scientificdirection是多对多的关系表,对于我们想要坚持的实体.在前端应用程序一切正常,但在索纳塔管理员我遇到了这个问题:(

jho*_*ter 28

如果你需要保留其他对象,那么Doc​​trine中的postPersist或postUpdate处理程序可能不是正确的选择.我今天遇到了同样的问题,因为我需要在该处理程序中生成一些消息条目.

此时的问题是postPersist处理程序 flush事件期间被调用,而不是之后.因此,您无法在此处保留其他对象,因为它们之后不会被刷新.此外,您不能在postPersist处理程序期间调用flush,因为这可能会导致ducplicate条目(正如您所经历的那样).

一种方法是使用doctrine中的onFlush处理程序,在此处记录:http://doctrine-orm.readthedocs.org/en/latest/reference/events.html#onflush

如果您需要插入数据库对象的ID,这只是有问题的,因为该实体尚未写入该处理程序中的数据库.如果你不需要那些id,你可以在教义中使用ofFlush事件.

对我来说,解决方案有点不同.我目前正在研究一个symfony2项目,并且需要插入数据库对象的ID(稍后用于回调和更新).

我在symfony2中创建了一个新服务,它基本上就像我的消息队列一样.在postPersist更新期间,我只填写队列中的条目.我在kernel.response其上注册了另一个处理程序,然后获取这些条目并将它们持久保存到数据库中.(有关这方面的内容:http://symfony.com/doc/current/cookbook/service_container/event_listener.html)

我希望我不要在这里偏离这个主题,但是因为这是我真正努力的事情,我希望有些人可能会觉得这很有用.

这方面的服务条目是:

 amq_messages_chain:
   class: Acme\StoreBundle\Listener\AmqMessagesChain

 amqflush:
   class: Acme\StoreBundle\Listener\AmqFlush
   arguments: [ @doctrine.orm.entity_manager, @amq_messages_chain, @logger ]
   tags:
     - { name: kernel.event_listener, event: kernel.response, method: onResponse, priority: 5 }

 doctrine.listener:
  class: Acme\StoreBundle\Listener\AmqListener
  arguments: [ @logger, @amq_messages_chain ]
  tags:
    - { name: doctrine.event_listener, event: postPersist }
    - { name: doctrine.event_listener, event: postUpdate }
    - { name: doctrine.event_listener, event: prePersist }
Run Code Online (Sandbox Code Playgroud)

您不能使用doctrine.listenerfor,因为这会导致循环依赖(因为您需要服务的实体管理器,但实体管理器需要服务....)

这就像一个魅力.如果您需要更多信息,请不要犹豫,我很高兴为此添加一些示例.


小智 27

Francesc的答案是错误的,因为postFlush事件中的变更集已经为空.jhoffrichter的第二个答案可行,但是有点矫枉过正.正确的方法是将实体持久化在postPersist事件中,并在postFlush事件中再次调用flush.但是,只有在postPersist事件中更改了某些内容时才必须执行此操作,否则您将创建无限循环.

public function postPersist(LifecycleEventArgs $args) {

    $entity = $args->getEntity();
    $em = $args->getEntityManager();

    if($entity instanceof FeedItemInterface) {
        $feed = new FeedEntity();
        $feed->setTitle($entity->getFeedTitle());
        $feed->setEntity($entity->getFeedEntityId());
        $feed->setType($entity->getFeedType());
        if($entity->isFeedTranslatable()) {
            $feed->getEnTranslation()->setTitle($entity->getFeedTitle('en'));
        }
        $em->persist($feed);
        $this->needsFlush = true;
    }
}

public function postFlush(PostFlushEventArgs $eventArgs)
{
    if ($this->needsFlush) {
        $this->needsFlush = false;
        $eventArgs->getEntityManager()->flush();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 嘿@chris,我不是想成为仇恨者,而是学说[文献](http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#postflush )"无法在[postFlush]的侦听器中安全地调用"EntityManager#flush()." 你对此有何看法? (11认同)
  • 这很有效 - 感谢分享! (4认同)
  • @chris,正如Ian上面所说,postFlush()中的flush()不安全.这真的有效吗?在我看来,事实并非如此. (3认同)