Doctrine inserting in postPersist event

Chcę dodać nowy Feed na encji persist I update. Piszę ten listener zdarzeń (postUpdate jest taki sam):

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();
    }
}

Ale mam

Naruszenie ograniczenia integralności: 1062 zduplikowany wpis "30-2" dla klucza "PRIMARY"

I w logu a mają dwie Wstawki:

Wstaw do interview_scientificdirection (interview_id, scientificdirection_id) wartości (?, ?) ([30,2]) WSTAWIĆ DO interview_scientificdirection (interview_id, scientificdirection_id) Wartości (?, ?) ([30,2])

Scientificdirection to tabela relacji wielu do wielu dla podmiotu, który chcemy utrzymać. W aplikacji frontend wszystko działa dobrze, ale w Sonata Admin mam taki problem: (

Author: Darryl Hein, 2012-04-29

5 answers

Od Doctrine 2.2 można dołączyć do zdarzenia 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();
}
 0
Author: Francesc Rosas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-27 17:24:05

ODPOWIEDŹ Francesc jest błędna, ponieważ zestawy zmian w zdarzeniu postFlush są już puste. Druga odpowiedź jhoffrichtera może zadziałać, ale jest przesadna. Właściwym sposobem jest zachowanie encji w zdarzeniu postPersist i ponowne wywołanie flush w zdarzeniu postFlush. Ale musisz to zrobić tylko wtedy, gdy zmieniłeś coś w zdarzeniu postPersist, w przeciwnym razie utworzysz nieskończoną pętlę.

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();
    }
}
 26
Author: chris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-21 01:53:54

Jeśli potrzebujesz utrzymywać dodatkowe obiekty, obsługa postpersist lub postUpdate w Doctrine nie jest, niestety, właściwym miejscem. Zmagałem się dzisiaj z tym samym problemem, ponieważ musiałem wygenerować kilka wpisów wiadomości w tym handlerze.

Problem w tym momencie polega na tym, że obsługa postPersist jest wywoływana Podczas zdarzenia flush, a nie po nim. Nie możesz więc utrzymywać tutaj dodatkowych obiektów, ponieważ nie są one później spłukiwane. Dodatkowo, nie możesz wywołać koloru podczas obsługi postpersyst, ponieważ może to prowadzić do ducplicate wpisy (jak doświadczyłeś).

Jednym ze sposobów jest użycie onflush handler z doctrine, udokumentowane tutaj: http://doctrine-orm.readthedocs.org/en/latest/reference/events.html#onflush

Jest to po prostu problematyczne, jeśli potrzebujesz wstawionych identyfikatorów obiektu database, ponieważ encja nie została jeszcze zapisana do bazy danych w tej funkcji obsługi. Jeśli nie potrzebujesz tych identyfikatorów, jesteś w porządku z wydarzeniem ofFlush w doktrynie.

Dla mnie rozwiązanie było trochę inne. Obecnie pracuję nad projektem symfony2 i potrzebowałem ID wstawionych obiektów bazy danych(dla wywołań zwrotnych i aktualizacji później).

Stworzyłem nową usługę w symfony2, która w zasadzie działa jak Kolejka dla moich wiadomości. Podczas aktualizacji postPersist, po prostu wypełniam wpisy w kolejce. Mam inny handler zarejestrowany na kernel.response, który następnie pobiera te wpisy i utrzymuje je w bazie danych. (Coś wzdłuż linii tego: http://symfony.com/doc/current/cookbook/service_container/event_listener.html )

Mam nadzieję, że nie odbiegam zbytnio od tematu tutaj, ale ponieważ jest to coś, z czym naprawdę się borykałem, mam nadzieję, że niektórzy ludzie mogą uznać to za przydatne.

Wpisy do tego serwisu to:

 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 }

Nie możesz używać doctrine.listener do tego celu, ponieważ prowadzi to do okrągłej zależności (ponieważ potrzebujesz menedżera encji dla usługi, ale menedżer encji potrzebuje obsługa....)

To zadziałało jak urok. Jeśli potrzebujesz więcej informacji na ten temat, nie wahaj się zapytać, z przyjemnością dodam kilka przykładów do tego.
 26
Author: jhoffrichter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-06 17:10:35

[[2]} cóż, oto jak zrobiłem w SF 2.0 i 2.2:

Klasa słuchacza:

<?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 }

Mam nadzieję, że pomoże;)

 2
Author: Marco,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-28 18:48:43

Rozwiązanie firmy jhoffrichter działa bardzo dobrze. Jeśli używasz poleceń konsoli, powinieneś dodać znacznik dla polecenia event.zakończyć. W przeciwnym razie nie działa ON wewnątrz poleceń konsoli. Zobacz 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 }
 2
Author: Stefan Bergfeld,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:03:08