Symfony2 - 在 onFlush 事件监听器中更改数据

4
我正在尝试使用KnpSnappyBundle生成PDF,并在生成我的Bill实体时将位置保存到Doctrine中。为此,我需要使用模板服务,但无法在实体内获取它。我研究了EventListener并尝试让其工作。这是我的config.yml:
services:
    bill.listener:
        class: MyCompany\CompanyBundle\EventListener\BillListener
        tags:
            - { name: doctrine.event_listener, event: onFlush }

我的监听器
namespace MyCompany\CompanyBundle\EventListener;

use Doctrine\ORM\Event\OnFlushEventArgs;

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

        foreach ($uow->getScheduledEntityInsertions() as $insertions) {
            foreach ($insertions as $entity) {
                if ($entity instanceof Bill) {
                    $entity->setFilename("test.pdf");

                    $md = $em->getClassMetadata(get_class($entity));
                    $uow->recomputeSingleEntityChangeSet($md, $entity);
                }
            }
        }
    }
}

我的账单实体有一个setFilename函数,我想在其中保存生成的账单的位置。但是我甚至无法使其工作。


嗨,我认为你忘记了Bill类的命名空间。 - a.aitboudad
1个回答

9

停止对UOW进行操作(这有点像黑客攻击),只需再次刷新即可:

配置

services:
    bill.listener:
        class: MyCompany\CompanyBundle\EventListener\BillListener
        arguments: ["@pdf_service"]
        tags :
            - { name: doctrine.event_subscriber, connection: default }

监听器

<?php

namespace MyCompany\CompanyBundle\EventListener;

use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\Event\PostFlushEventArgs;

use MyCompany\CompanyBundle\Service\MyPDFService;
use MyCompany\CompanyBundle\Entity\Bill;

class BillListener implements EventSubscriber
{
    protected $pdfs;
    protected $bills;

    public function __construct(MyPDFService $pdfs)
    {
        $this->pdfs = $pdfs;
    }

    public function getSubscribedEvents()
    {
        return [
            'onFlush',
            'postFlush'
        ];
    }

    public function onFlush(OnFlushEventArgs $event)
    {
        $this->bills = [];
        /* @var $em \Doctrine\ORM\EntityManager */
        $em = $event->getEntityManager();
        /* @var $uow \Doctrine\ORM\UnitOfWork */
        $uow = $em->getUnitOfWork();

        foreach ($uow->getScheduledEntityInsertions() as $entity) {

            if ($entity instanceof Bill) {

                $this->bills[] = $entity;
            }
        }
    }

    public function postFlush(PostFlushEventArgs $event)
    {
        if (!empty($this->bills)) {

            /* @var $em \Doctrine\ORM\EntityManager */
            $em = $event->getEntityManager();

            foreach ($this->bills as $bill) {

                /* @var $bill \MyCompany\CompanyBundle\Entity\Bill */
                $this->pdfs->generateFor($bill);
                $em->persist($bill);
            }

            $em->flush();
        }
    }
}

那对我有用。不得不更改getSubscribedEvents函数以返回数组('onFlush','postFlush')。 - user2898771
抱歉,但是“插入”只会在实体首次持久化时发生一次。无论如何,可以使用某个属性作为标志来避免循环。 - coma
1
这个解决方案不会破坏操作的原子性吗?如果第二次刷新失败怎么办?好的,在这种情况下,PDF文件将不会生成,但是如果第二次刷新非常重要呢?这可能会导致不一致性,对吧? - Byscripts
1
那么,你应该好好处理它,也许在这种情况下,你真正需要的是一项服务,以你想要的方式管理你的操作,只需一次交易即可。 - coma
一旦Doctrine触发了此监听器,您应该将其删除,以避免潜在的无限循环。可以使用类似$em->getEventManager()->removeEventListener(.....)的方法来实现。 - Thomas Decaux
3
在postFlush中调用flush()似乎不是一个好主意。“EntityManager#flush()无法在“”内安全调用:http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#postflush - jrjohnson

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