エンティティの永続化と更新に新しいフィードアイテムを追加します。このイベントリスナーを記述します(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キー 'PRIMARY'のエントリ'30 -2 'が重複しています
ログには、2つの挿入があります。
INSERT INTO Interview_scientificdirection(interview_id、ScientificDirection_id)VALUES(?、?)([30,2])INSERT INTO Interview_scientificdirection(Interview_id、ScientificDirection_id)VALUES(?、?)([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();
}
追加のオブジェクトを永続化する必要がある場合、DoctrineのpostPersistまたはpostUpdateハンドラーは残念ながら適切な場所ではありません。メッセージを生成する必要があったため、今日同じ問題で苦労しましたそのハンドラのエントリ。
この時点での問題は、postPersistハンドラーがduringフラッシュイベントで呼び出され、その後では呼び出されないことです。追加のオブジェクトは後でフラッシュされないため、ここでは永続化できません。さらに、postPersistハンドラーの間、フラッシュを呼び出すことはできません。これは、(経験したように)重複したエントリにつながる可能性があるためです。
ここに記載されているdoctrineのonFlushハンドラーを使用する方法があります。 http://doctrine-orm.readthedocs.org/en/latest/reference/events.html#onflush
エンティティはまだハンドラーでデータベースに書き込まれていないため、データベースオブジェクトの挿入されたIDが必要な場合、これは問題になります。これらのIDが必要ない場合は、Doctrineの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 }
これにはdoctrine.listener
を使用できません。循環依存が発生するためです(サービスにはエンティティマネージャーが必要ですが、エンティティマネージャーにはサービスが必要です...)。
それは魅力のように働きました。それについてもっと情報が必要な場合は、遠慮なく尋ねてください。これにいくつかの例を追加できてうれしいです。
Francescからの回答は間違っています。postFlushイベントのチェンジセットがすでに空になっているためです。 jhoffrichterの2番目の答えは機能しますが、やり過ぎです。正しい方法は、エンティティを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();
}
}
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 }
それが役に立てば幸い ;)