【问题标题】:Creating preUpdate or preFlush event with Event Listener使用事件监听器创建 preUpdate 或 preFlush 事件
【发布时间】:2016-04-16 14:40:45
【问题描述】:

我有一个工作正常的 onFlush() 事件,但我需要做的是把它变成 preFlush() 或 preUpdate() 都可以接受。我做了 preFlush() 但由于某种原因它什么也没做。甚至没有错误。我错过了什么?

测试:我将exit 放入preFlush() 以查看它是否被调用。结果是:1 所以foreach() 永远不会运行!它是一个空数组。我还测试了 preUpdate() 以及其中的所有行都运行但没有插入数据。

public function preFlush(PreFlushEventArgs $args)
{
    $em = $args->getEntityManager();
    $uow = $em->getUnitOfWork();
    echo '1';
    foreach ($uow->getScheduledEntityUpdates() as $entity) {
        echo '2';
        if ($entity instanceof User) {
            echo '3';
        }
    }
    exit;
}

我在阅读documentation 后创建了它们。

service.yml

services:
    entity.event_listener.user:
        class:  Site\FrontBundle\EventListener\Entity\UserListener
        tags:
            - { name: doctrine.event_listener, event: preUpdate }
            - { name: doctrine.event_listener, event: onFlush }
            - { name: doctrine.event_listener, event: preFlush }

处理 onFlush() 示例:

class UserListener
{
    public function onFlush(OnFlushEventArgs $args)
    {
        $em = $args->getEntityManager();
        $uow = $em->getUnitOfWork();

        foreach ($uow->getScheduledEntityUpdates() as $entity) {
            if ($entity instanceof User) {
                $userLog = new UserLog();
                $userLog->setDescription($entity->getId() . ' being updated.');

                $em->persist($userLog);

                // Instead of $em->flush() cos we're already in flush process
                $userLogMetadata = $em->getClassMetadata(get_class($userLog));
                $uow->computeChangeSet($userLogMetadata, $userLog);
            }
        }
    }
}

不工作的 preFlush() 示例:

class UserListener
{
    public function preFlush(PreFlushEventArgs $args)
    {
        $em = $args->getEntityManager();
        $uow = $em->getUnitOfWork();

        foreach ($uow->getScheduledEntityUpdates() as $entity) {
            if ($entity instanceof User) {
                $userLog = new UserLog();
                $userLog->setDescription($entity->getId() . ' being updated.');

                $em->persist($userLog);

                // Instead of $em->flush() cos we're already in flush process
                $userLogMetadata = $em->getClassMetadata(get_class($userLog));
                $uow->computeChangeSet($userLogMetadata, $userLog);
            }
        }
    }
}

不工作的 preUpdate() 示例

class UserListener
{
    public function preUpdate(LifecycleEventArgs $args)
    {
        $entity = $args->getEntity();
        $em = $args->getEntityManager();
        $uow = $em->getUnitOfWork();

        if ($entity instanceof User) {
            $userLog = new UserLog();
            $userLog->setDescription($entity->getId() . ') been updated.');

            $em = $args->getEntityManager();
            $em->persist($userLog);
            $userLogMetadata = $em->getClassMetadata(get_class($userLog));
            $uow->computeChangeSet($userLogMetadata, $userLog);
        }
    }
}

【问题讨论】:

  • 可以添加服务配置部分吗?
  • @devsheeep - 更新了帖子。
  • 能否显示调用persist 和flush 方法的代码?
  • 以控制器为例。例如。 $user = new User; $user->setName('abc'); $em->flush(); 没有持久化,因为这是 symfony2 的更新操作。
  • 在 preFlush 中你应该使用 $args->getEntity() 而不是 $uow->getScheduledEntityUpdates()。

标签: symfony doctrine-orm


【解决方案1】:

解决方案:

诀窍是,在preUpdate() 之后在postFlush() 事件中坚持。

注意:虽然这可能不是最好的解决方案,但它可以回答问题,但可以使用事件订阅者或简单的onFlush() -> @987654325 @ 在事件监听器中。

Service.yml

services:

    entity.event_listener.user_update:
        class:  Site\FrontBundle\EventListener\Entity\UserUpdateListener
        tags:
            - { name: doctrine.event_listener, event: preUpdate }
            - { name: doctrine.event_listener, event: postFlush }

事件监听器

<?php

namespace Site\FrontBundle\EventListener\Entity;

use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\PostFlushEventArgs;
use Site\FrontBundle\Entity\User;
use Site\FrontBundle\Entity\UserLog;

class UserUpdateListener
{
    private $log = array();

    public function preUpdate(LifecycleEventArgs $args)
    {
        $entity = $args->getEntity();

        // False check is compulsory otherwise duplication occurs
        if (($entity instanceof User) === false) {
            $userLog = new UserLog();
            $userLog->setDescription($entity->getId() . ' being updated.');

            $this->log[] = $userLog;
        }
    }

    public function postFlush(PostFlushEventArgs $args)
    {
        if (! empty($this->log)) {
            $em = $args->getEntityManager();
            foreach ($this->log as $log) {
                $em->persist($log);
            }
            $em->flush();
        }
    }
} 

【讨论】:

  • 不要这样做。在postFlush 事件中调用EntityManager#flush()不安全。参看。 Doctrine Documentation
【解决方案2】:

阅读文档,

http://doctrine-orm.readthedocs.org/en/latest/reference/events.html#onflush

它没有提到preFlush 有关于更改的信息(我的意思是 entityManager)

如果您查看Doctrine\ORM\UnitOfWork,您会发现更改集是在preFlush 事件之后计算的,因此如果您想与更改的实体进行交互,您应该使用onFlush

// Raise preFlush
    if ($this->evm->hasListeners(Events::preFlush)) {
        $this->evm->dispatchEvent(Events::preFlush, new PreFlushEventArgs($this->em));
    }

// Compute changes done since last commit.
if ($entity === null) {
    $this->computeChangeSets();
} elseif (is_object($entity)) {
    $this->computeSingleEntityChangeSet($entity);
} elseif (is_array($entity)) {
    foreach ($entity as $object) {
        $this->computeSingleEntityChangeSet($object);
    }
}

【讨论】:

  • 如果onFlush()监听函数中的某些条件失败,如何中止刷新操作?
【解决方案3】:

亲爱的BentCoder
我正在使用 Symfony 2.7 版本。在像您的帖子这样的事件侦听器中使用 $em->flush 时,会发生此错误: click-here-to-see-bug-description

这是我的解决方案:

Service.yml

services:
    app.listener:
        class: AppBundle\EventListener\DoctrineListener
        arguments: ["@service_container"]
        tags:
            - { name: doctrine.event_listener, event: preUpdate, method: preUpdate }
            - { name: doctrine.event_listener, event: postUpdate, method: postUpdate }

事件监听器

namespace AppBundle\EventListener;

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

use AppBundle\Entity;

/**
 * Log activity on website
 * Class DoctrineListener
 * @package AppBundle\EventListener
 */
class DoctrineListener
{
    /**
     * @var ContainerInterface
     */
    private $_container;

    /**
     * @var Array
     */
    private $_activities;

    /**
     * DoctrineListener constructor.
     * @param ContainerInterface $container
     */
    public function __construct(ContainerInterface $container)
    {
        $this->_container = $container;
    }

    /**
     * @param LifecycleEventArgs $args
     */
    public function preUpdate(LifecycleEventArgs $args)
    {
        $entityManager = $args->getEntityManager();
        $entity = $args->getEntity();
        $activityEntity = new Entity\Activity();
        $activityEntity->setAction(Entity\Activity::ACTION_EDIT);
        $activityEntity->setActionAt(new \DateTime());
        $activityEntity->setIpAddress($this->_container->get('request')->getClientIp());
        switch (true) {
            case $entity instanceof Entity\Goods:
                $repository = $entityManager->getRepository('AppBundle:Goods');
                $activityEntity->setType(Entity\Activity::TYPE_GOODS);
                $message = 'User: <strong>%s</strong> sửa mẫu hàng hóa <strong>%s</strong>';
                break;
            default:
                return;
        }
        if (isset($repository) && $args->getEntityChangeSet()) {
            $user = $this->_container->get('security.context')->getToken()->getUser();
            $recordBefore = clone $entity;
            foreach ($args->getEntityChangeSet() as $key => $value) {
                $method = 'set'.ucfirst($key);
                $recordBefore->$method($value[0]);
            }
            $activityEntity->setFosUser($user);
            $activityEntity->setRecordBefore(serialize($recordBefore));
            $activityEntity->setRecordAfter(serialize($entity));
            $activityEntity->setMessage(
                sprintf(
                    $message,
                    $user->getUserProfile()->getFullName(),
                    (string) $recordBefore
                )
            );
            $this->_activities[] = $activityEntity;
        }
        return;
    }

    /**
     * @param LifecycleEventArgs $args
     */
    public function postUpdate(LifecycleEventArgs $args)
    {
        if (sizeof($this->_activities)) {
            $entityManager = $args->getEntityManager();
            foreach ($this->_activities as $activity) {
                $entityManager->persist($activity);
            }
            $entityManager->flush();
        }
    }
}

希望这会对某人有所帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-30
    • 2013-08-02
    • 2012-01-22
    • 1970-01-01
    相关资源
    最近更新 更多