Symfony onFlush Doctrine 监听器

3

你好,我有一个onFlush监听器:

<?php

namespace FM\AppBundle\EventListener;

use FM\AdminBundle\Entity\Address\DeliveryAddress;
use Doctrine\ORM\Event\OnFlushEventArgs;

class DeliveryAddressListener
{
    /**
     * @param OnFlushEventArgs $args
     */
    public function onFlush(OnFlushEventArgs $args)
    {
        $em = $args->getEntityManager();
        $uow = $em->getUnitOfWork();

        foreach ($uow->getScheduledEntityUpdates() as $entity) {
            if ($entity instanceof DeliveryAddress) {
                $this->addPostalToUser($entity, $args);
            }
        }
    }

    /**
     * @param DeliveryAddress  $deliveryAddress
     * @param OnFlushEventArgs $args
     */
    public function addPostalToUser(DeliveryAddress $deliveryAddress, OnFlushEventArgs $args)
    {
        $em = $args->getEntityManager();
        $user = $deliveryAddress->getOwner();

        $user->setPostalCode($deliveryAddress->getZipCode());
    }
}

service.yml:

delivery_address.listener:
    class: FM\AppBundle\EventListener\DeliveryAddressListener
    tags:
        - { name: doctrine.event_listener, event: onFlush }

我尝试将新的邮政编码设置给用户。但好像没有成功。
即使我添加了$em->persist($user)
我查看了这个文档:http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#onflush 但我不明白如何根据以下解释使其工作: 如果在 onFlush 中创建和持久化一个新实体,则仅调用 EntityManager # persist() 是不够的。您还必须执行额外的调用 $unitOfWork->computeChangeSet($classMetadata, $entity)。
3个回答

6

当操作字段时,应该在preUpdate/prePersist中进行。

AppBundle/EventSubscriber/EntitySubscriber.php

namespace AppBundle\EventSubscriber;

use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\OnFlushEventArgs;

class EntitySubscriber implements EventSubscriber
{
    private $now;

    public function __construct()
    {
        $this->now = \DateTime::createFromFormat('Y-m-d h:i:s', date('Y-m-d h:i:s'));
    }

    public function getSubscribedEvents()
    {
        return [
            'prePersist',
            'preUpdate'
        ];
    }

    public function prePersist(LifecycleEventArgs $args)
    {
        $entity        = $args->getEntity();
        $entityManager = $args->getEntityManager();

        if (method_exists($entity, 'setCreatedAt')) {
            $entity->setUpdatedAt($this->now);
        }

        if (method_exists($entity, 'setUpdatedAt')) {
            $entity->setUpdatedAt($this->now);
        }
    }

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

        if (method_exists($entity, 'setUpdatedAt')) {
            $entity->setUpdatedAt($this->now);
        }
    }
}

services.yml

    app.entity_subscriber:
        class: AppBundle\EventSubscriber\EntitySubscriber
        tags:
            - { name: doctrine.event_subscriber, connection: default }

5
如果您需要在监听器中创建对象、持久化对象并将其刷新,那么tlorens的答案行不通,因为Doctrine文档指出必须使用onFlush事件来完成此操作。
最初的问题是如何遵循文档建议使其正常工作: “如果您在onFlush中创建和持久化新实体,则调用EntityManager#persist()不足够。您必须执行一个额外的调用$unitOfWork->computeChangeSet($classMetadata, $entity)。”
以下是实现此目的的方法:
/**
 * @param OnFlushEventArgs $eventArgs
 */
public function onFlush(OnFlushEventArgs  $eventArgs)
{
    $em = $eventArgs->getEntityManager();
    $uow = $em->getUnitOfWork();

    foreach ($uow->getScheduledEntityUpdates() as $entity) {
        if ($entity instanceof User) {
            $uow->computeChangeSets();
            $changeSet = $uow->getEntityChangeSet($entity);
            // In this exemple, User has a boolean property 'enabled' and a log will be created if it is passed to 'false'
            if ($changeSet && isset($changeSet['enabled']) && $changeSet['enabled'][1] === false) {
                $log = new Log();
                $em->persist($log);
                $uow->computeChangeSet($em->getClassMetadata(get_class($log)), $log);
            }
        }
    }

有些组件使用了我解决问题的完全相同的逻辑。我的答案是将对象操作放在正确的事件中。不确定为什么你要在onFlush中进行这个复杂的操作,当它可以在其他地方完美地完成。 - tlorens

-1

当我使用它时,它有效:

// Remove event, if we call $this->em->flush() now there is no infinite recursion loop!
$eventManager->removeEventListener('onFlush', $this);

My Listener

namespace FM\AppBundle\EventListener;

use FM\AdminBundle\Entity\Address\DeliveryAddress;
use Doctrine\ORM\Event\OnFlushEventArgs;

class DeliveryAddressListener
{
    /**
     * @param OnFlushEventArgs $args
     */
    public function onFlush(OnFlushEventArgs $args)
    {
        $em = $args->getEntityManager();
        $uow = $em->getUnitOfWork();
        $eventManager = $em->getEventManager();

        // Remove event, if we call $this->em->flush() now there is no infinite recursion loop!
        $eventManager->removeEventListener('onFlush', $this);

        foreach ($uow->getScheduledEntityUpdates() as $entity) {
            if ($entity instanceof DeliveryAddress) {
                $this->addPostalToUser($entity, $args);
            }
        }
    }

    /**
     * @param DeliveryAddress  $deliveryAddress
     * @param OnFlushEventArgs $args
     */
    public function addPostalToUser(DeliveryAddress $deliveryAddress, OnFlushEventArgs $args)
    {
        $em = $args->getEntityManager();
        $user = $deliveryAddress->getOwner();

        $user->setPostalCode($deliveryAddress->getZipCode());
        $em->flush();
    }
}


6
您不应该进行数据刷新操作。相反,您应该使用UOW函数调用重新计算。请参考此答案中的示例:http://stackoverflow.com/questions/34342324/doctrine-eventlistener-onflush-does-not-save-original-entity/34342751#34342751。问题在于您的数据刷新操作会再次调用数据刷新操作,从而导致监听器被重复触发。 - Richard
即使我使用 $eventManager->removeEventListener('onFlush', $this); - Kevin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接