Symfony 中的 PostPersist 和 PostFlush 无限循环
PostPersist & PostFlush infinite loop in Symfony
我正在尝试 link 在用户进入数据库后向用户提供咖啡配置文件。
此咖啡配置文件数据在一个会话中,我在 postFlush 中使用此会话。
但是这段代码正在创建一个无限循环,我不知道为什么:
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 }
是什么原因导致此循环,我该如何解决?
您在 postPersist 事件中调用 $em->flush()
,在 docs 中声明:
postFlush is called at the end of EntityManager#flush().
EntityManager#flush() can NOT be called safely inside its listeners.
您应该使用其他事件,例如 prePersist
或 postPersist
。
如果可能,尽量避免在一个请求中出现多个 flush()
。
顺便说一句,没有必要这样做,因为您的用户对象已经包含在您的 $user 变量中。
$us =
$em->getRepository('AppBundle:User')->findOneById($this->user->getId());
在你的 postFlush
事件中,你又在脸红了。这就是导致无限循环的原因,因为每次调用 flush
方法时都会触发 postFlush
事件。
我不确定你想要达到什么目的,但你的目标是在每次保存用户时创建一次咖啡消费,你可以在你的方法开始时添加这样的测试:
$entity = $args->getObject();
if (!$entity instanceof User) {
return;
}
这将防止无限循环。
还有一些事情:
- 你的
postPersist
方法好像没用。每次持久化对象时都会调用它,因此您的 $this->user
属性不一定是用户对象。
- 如果您需要用户,则不必在数据库中获取它。只需使用
$args->getObject()
即可获取刷新的实体。除了上面的测试之外,您将确定该方法将 return 您是一个 User 对象。
- 在你的 Doctrine 监听器中检查用户是否登录不是一个很好的做法。这不是 class 应该做的。
- 不要在构造函数中注入容器。只注入你需要的东西(在这种情况下......什么都没有?)
我正在尝试 link 在用户进入数据库后向用户提供咖啡配置文件。 此咖啡配置文件数据在一个会话中,我在 postFlush 中使用此会话。
但是这段代码正在创建一个无限循环,我不知道为什么:
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 }
是什么原因导致此循环,我该如何解决?
您在 postPersist 事件中调用 $em->flush()
,在 docs 中声明:
postFlush is called at the end of EntityManager#flush(). EntityManager#flush() can NOT be called safely inside its listeners.
您应该使用其他事件,例如 prePersist
或 postPersist
。
如果可能,尽量避免在一个请求中出现多个 flush()
。
顺便说一句,没有必要这样做,因为您的用户对象已经包含在您的 $user 变量中。
$us = $em->getRepository('AppBundle:User')->findOneById($this->user->getId());
在你的 postFlush
事件中,你又在脸红了。这就是导致无限循环的原因,因为每次调用 flush
方法时都会触发 postFlush
事件。
我不确定你想要达到什么目的,但你的目标是在每次保存用户时创建一次咖啡消费,你可以在你的方法开始时添加这样的测试:
$entity = $args->getObject();
if (!$entity instanceof User) {
return;
}
这将防止无限循环。
还有一些事情:
- 你的
postPersist
方法好像没用。每次持久化对象时都会调用它,因此您的$this->user
属性不一定是用户对象。 - 如果您需要用户,则不必在数据库中获取它。只需使用
$args->getObject()
即可获取刷新的实体。除了上面的测试之外,您将确定该方法将 return 您是一个 User 对象。 - 在你的 Doctrine 监听器中检查用户是否登录不是一个很好的做法。这不是 class 应该做的。
- 不要在构造函数中注入容器。只注入你需要的东西(在这种情况下......什么都没有?)