11

I have a preUpdate listener in my app. When it is fired I want it to create some additional records. A simplified example of the basic functionality is below. In this current implementation it would appear that the new events are not being persisted. Are there other calls I need to be making here? Thanks.

public function preUpdate(Event\LifecycleEventArgs $eventArgs)
{
    $em = $eventArgs->getEntityManager();
    $uow = $em->getUnitOfWork();
    $entity = $eventArgs->getEntity();

    $updateArray = $eventArgs->getEntityChangeSet();

    //Updates
    if (($entity instanceof Bam) === false) {
        $thing = new OtherThing();
        $thing->setFoo('bar');

        $uow->persist($thing);
    }

    $uow->computeChangeSets();
}
keybored
  • 5,194
  • 13
  • 45
  • 70

1 Answers1

26

The key is to persist them after the flush:

<?php

namespace Comakai\CQZBundle\Handler;

use Symfony\Component\DependencyInjection\ContainerInterface;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event;

/**
 * 
 */
class YourHandler implements EventSubscriber
{
    protected $things = [];

    public function getSubscribedEvents()
    {
        /**
         * @todo Check if this is running in the console or what...
         */
        if (isset($_SERVER['HTTP_HOST'])) {

            return [
                'preUpdate',
                'postFlush'
            ];
        }

        return [];
    }

    public function preUpdate(Event\LifecycleEventArgs $eventArgs)
    {
        $em = $eventArgs->getEntityManager();
        $uow = $em->getUnitOfWork();
        $entity = $eventArgs->getEntity();

        $updateArray = $eventArgs->getEntityChangeSet();

        //Updates
        if (($entity instanceof Bam) === false) {

            $thing = new OtherThing();
            $thing->setFoo('bar');

            $this->things[] = $thing;
        }
    }

    public function postFlush(Event\PostFlushEventArgs $event)
    {
        if(!empty($this->things)) {

            $em = $event->getEntityManager();

            foreach ($this->things as $thing) {

                $em->persist($thing);
            }

            $this->things = [];
            $em->flush();
        }
    }
}
coma
  • 16,429
  • 4
  • 51
  • 76
  • 1
    I try this, but I have "PHP Fatal error: Maximum function nesting level of '1000' reached," – Igor Mancos Jan 31 '14 at 11:48
  • 2
    Detect bug: Is very important to set "$this->things = [];" before "$em->flush();" – Igor Mancos Jan 31 '14 at 11:56
  • Sorry, it happened to me too!, updating my answer, thank you Igor. – coma Jan 31 '14 at 20:26
  • A possible workaround for the fatal error @IgorLadela: http://stackoverflow.com/questions/16980191/symfony2-maximum-function-nesting-level-of-100-reached-aborting-with-doctrin/31472299#31472299 – webDEVILopers Jul 17 '15 at 09:44
  • So much reading to finally find your solution which is the real one ! – Frédéric Klee Apr 13 '16 at 15:33
  • @coma What is the purpose of `isset($_SERVER['HTTP_HOST'])` here? – Jules Lamur Jan 24 '17 at 18:09
  • @Kadriles, avoid it from running on console triggered tasks and only allow it when it's reached by an http request (pretty nasty, that's why it has a todo comment). – coma Jan 25 '17 at 09:36
  • 2
    According to the current [Doctrine documentation](http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#postflush): `EntityManager#flush()` can NOT be called safely inside of `postFlush()`. – Trappar Jun 26 '17 at 08:37
  • @Trappar, thanks for the update (I haven't touched PHP for years now). Consider adding a new answer (I'll upvote yours) – coma Jun 26 '17 at 08:41