Marco Koopman
Marco Koopman

Reputation: 136

PostPersist & PostFlush infinite loop in Symfony

I'm trying to link a coffee profile to a user after the user is in the database. This coffee profile data is in a session and I'm using this session in the postFlush.

However This code is creating an infinite loop and I don't know why:

UserListener.php:

<?php

namespace AppBundle\EventListener;

use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\HttpFoundation\Session\Session;
use AppBundle\Entity\Consumption;
use AppBundle\Entity\CoffeeOption;
use AppBundle\Entity\Consumeable;
use AppBundle\Entity\MomentPreference;

use Doctrine\ORM\Event\LifecycleEventArgs;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Doctrine\ORM\Event\PostFlushEventArgs;

class UserListener
{
    private $container;
    private $user;

    public function __construct(ContainerInterface $container = null)
    {
        $this->container = $container;
    }

    public function postPersist(LifecycleEventArgs $args)
    {
        $user = $args->getEntity();

        $this->user = $user;
    }

    public function postFlush(PostFlushEventArgs $args)
    {
        $session = new Session();

        if($session) {

            $em = $args->getEntityManager();

            $us = $em->getRepository('AppBundle:User')->findOneById($this->user->getId());

            $consumption = $session->get('consumption');
            $coffee = $session->get('coffee');
            $moment = $session->get('moment');

            $consumption->setUser($us);

            //dummy data for the day, later this needs to be turned into datetime
            $moment->setDay('monday');
            $moment->setConsumption($consumption);

            $em->persist($consumption);
            $em->persist($coffee);
            $em->persist($moment);

            $em->flush();        

        } else {
            return $this->redirectToRoute('fos_user_registration_register');
        }
    }

}

Services.yml:

    zpadmin.listener.user:
    class: AppBundle\EventListener\UserListener
    arguments: ['@service_container']
    tags:
        - { name: doctrine.event_listener, event: postPersist }
        - { name: doctrine.event_listener, event: postFlush }

What is causing this loop and how can I fix it?

Upvotes: 1

Views: 7205

Answers (3)

Yasin Cetintas
Yasin Cetintas

Reputation: 36

This example try please

    use Doctrine\Bundle\DoctrineBundle\EventSubscriber\EventSubscriberInterface;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\Event\PostFlushEventArgs;
use Doctrine\ORM\Events;
use Symfony\Component\DependencyInjection\ContainerInterface;

class DatabaseActivitySubscriber implements EventSubscriberInterface
{
    /**
     * @var array
     */
    protected array $changeUpdateDataEntityList = [];

    /**
     * @var array
     */
    protected array $changeDeleteDataEntityList = [];

    /**
     * @var \Symfony\Component\DependencyInjection\ContainerInterface
     */
    protected ContainerInterface $container;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    public function getSubscribedEvents(): array
    {
        return [
            Events::postFlush,
            Events::onFlush
        ];
    }

    public function postFlush(PostFlushEventArgs $args): void
    {
        foreach ($this->changeUpdateDataEntityList as $entity) {
            // Do something
        }
    }

    public function onFlush(OnFlushEventArgs $eventArgs): void
    {
        $em = $eventArgs->getObjectManager();
        $uow = $em->getUnitOfWork();

        foreach ($uow->getScheduledEntityInsertions() as $entity) {
            // Do something
        }

        foreach ($uow->getScheduledEntityUpdates() as $entity) {
            // Do something
        }

        foreach ($uow->getScheduledEntityDeletions() as $entity) {
            // Do something
        }
    }

}

Upvotes: 1

fxbt
fxbt

Reputation: 2596

In your postFlush event, you're flushing again. That's what causing the infinite loop because the postFlush event is triggered every time you're calling the flush method.

I'm not sure what you're trying to achieve but your goal is to create a coffee consumption everytime you're saving a user, you can add a test like this one at the start of your method:

$entity = $args->getObject();

if (!$entity instanceof User) {
    return;
}

This will prevent the infinite loop.

And a few more things:

  • Your postPersist method seems useless. It'll be called every time an object is persisted so your $this->user propety will not necessarily be a User object.
  • If you need the user, you don't have to fetch it in your database. Simply use $args->getObject() to get the flushed entity. In addition with the test above, you'll be sure the method will return you a User object.
  • This is not a very good practice to check if the user is logged in in your Doctrine listener. This is not what the class is supposed to do.
  • Don't inject the container in your constructor. Inject only what you need (in this case... nothing ?)

Upvotes: 2

Iwan Wijaya
Iwan Wijaya

Reputation: 2157

You are calling $em->flush() inside your postPersist event, in the docs it is stated that:

postFlush is called at the end of EntityManager#flush(). EntityManager#flush() can NOT be called safely inside its listeners.

You should use other events like prePersist or postPersist.

If possible try to avoid multiple flush() on a single request.

by the way, there is no need to do this, because your user object is already contained inside your $user variable.

$us = $em->getRepository('AppBundle:User')->findOneById($this->user->getId());

Upvotes: 1

Related Questions