我想在实体上添加新的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();
}
}
但我知道了
违反完整性约束:1062密钥的重复条目“ 30-2”'PRIMARY'
并且在日志中有两个插入:
[插入INST访谈_科学方向(interview_id,Scientificdirection_id)值(?,?)([30,2])插入面试科学方向(interview_id,科学方向ID)值(?,?)([30,2])
scientificdirection是实体要保持的多对多关系表。在前端应用程序中,一切正常,但是在Sonata Admin中,我遇到了这个问题:(
从Doctrine 2.2开始,您可以将侦听器附加到postFlush事件:
function postFlush($args) {
$em = $args->getEntityManager();
foreach ($em->getUnitOfWork()->getScheduledEntityInsertions() as $entity) {
if ($entity instanceof FeedItemInterface) {
$feed = new FeedEntity;
...
$em->persist($feed);
}
}
$em->flush();
}
Francesc的答案是错误的,因为postFlush事件中的变更集已经为空。霍夫里希特的第二个答案可能奏效,但过于矫kill过正。正确的方法是将实体保留在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();
}
}
如果您需要保留其他对象,可悲的是,Doctrine中的postPersist或postUpdate处理程序不是正确的选择。今天,我遇到了同样的问题,因为我需要在该处理程序中生成一些消息条目。
此时的问题是,postPersist处理程序称为during刷新事件,而不是之后的事件。因此,您无法在此处保留其他对象,因为之后它们不会被刷新。另外,您不能在postPersist处理程序期间调用flush,因为这可能会导致条目重复(如您所知)。
一种方法是使用原则中的onFlush处理程序,在此处记录:http://doctrine-orm.readthedocs.org/en/latest/reference/events.html#onflush
如果您需要插入数据库对象的ID,这是有问题的,因为尚未在该处理程序中将实体写入数据库。如果不需要这些ID,则可以使用doctrine中的onFlush事件。
对我来说,解决方案有些不同。我目前正在研究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 }
您不能为此使用doctrine.listener
,因为这会导致循环依赖(因为您需要服务的实体管理器,但是实体管理器需要服务...。]
这就像一种魅力。如果您需要更多有关此的信息,请不要犹豫,我很高兴在此添加一些示例。
jhoffrichter的解决方案效果很好。如果使用控制台命令,则应为事件command.terminate添加标签。否则,它在控制台命令中不起作用。参见https://stackoverflow.com/a/19737608/1526162
config.yml
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 }
- { name: kernel.event_listener, event: command.terminate, method: onResponse }
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 }
嗯,这是我在SF 2.0和2.2中所做的事情:
侦听器类:
<?php
namespace YourNamespace\EventListener;
use Doctrine\ORM\Mapping\PostPersist;
/*
* ORMListener class
*
* @author: Marco Aurélio Simão
* @description: Listener para realizar operações em qualquer objeto manipulado pelo Doctrine 2.2
*/
use Doctrine\ORM\UnitOfWork;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\Common\EventArgs;
use Doctrine\ORM\Mapping\PrePersist;
use Doctrine\ORM\Event\PostFlushEventArgs;
use Doctrine\ORM\Mapping\PostUpdate;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Event\PreFlushEventArgs;
use Enova\EntitiesBundle\Entity\Entidades;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Enova\EntitiesBundle\Entity\Tagged;
use Enova\EntitiesBundle\Entity\Tags;
class ORMListener
{
protected $extra_update;
public function __construct($container)
{
$this->container = $container;
$this->extra_update = false;
}
public function onFlush(OnFlushEventArgs $args)
{
$securityContext = $this->container->get('security.context');
$em = $args->getEntityManager();
$uow = $em->getUnitOfWork();
$cmf = $em->getMetadataFactory();
foreach ($uow->getScheduledEntityInsertions() AS $entity)
{
$meta = $cmf->getMetadataFor(get_class($entity));
$this->updateTagged($em, $entity);
}
foreach ($uow->getScheduledEntityUpdates() as $entity)
{
$meta = $cmf->getMetadataFor(get_class($entity));
$this->updateTagged($em, $entity);
}
}
public function updateTagged($em, $entity)
{
$entityTags = $entity->getTags();
$a = array_shift($entityTags);
//in my case, i have already sent the object from the form, but you could just replace this part for new Object() etc
$uow = $em->getUnitOfWork();
$cmf = $em->getMetadataFactory();
$meta = $cmf->getMetadataFor(get_class($a));
$em->persist($a);
$uow->computeChangeSet($meta, $a);
}
}
Config.yml:
services:
updated_by.listener:
class: YourNamespace\EventListener\ORMListener
arguments: [@service_container]
tags:
- { name: doctrine.event_listener, event: onFlush, method: onFlush }
希望有帮助;)
在刷新后事件之后,请勿呼叫$em->flush()
。>