Symfony& Doctrine:使用Event Listener记录表中的更改

时间:2017-06-19 22:25:29

标签: php symfony doctrine-orm

在Symfony 3项目中,我有一个实体,我想审核一些属性的变化,所以我可以创建一个事件监听器来存储它们。

实体或多或少如下:

  • ReceivedEmail:agent和caseDetail是我要审核的属性
  • ReceivedEmailChange:previousAgent,currentAgent和previousCaseDetail and currentCaseDetail

EventListener如下所示

    /**
     * @param OnFlushEventArgs $args
     */
    public function onFlush(OnFlushEventArgs $args)
    {
        /** @var ReceivedEmail $entity */
        $entityManager = $args->getEntityManager();
        $unitOfWork = $entityManager->getUnitOfWork();

        $updates = $unitOfWork->getScheduledEntityUpdates();

        foreach ($updates as $entity) {

            if ($entity instanceof ReceivedEmail) {

                $changes = $unitOfWork->getEntityChangeSet($entity);

                $this->receivedEmailChanges[] = $this->receivedEmailChangeManager
                    ->getReceivedEmailChanges($entity, $changes);
            }
        }
    }

public function postFlush(PostFlushEventArgs $args)
    {
        $em = $args->getEntityManager();

        $i = 0;
        foreach($this->receivedEmailChanges as $receivedEmailChange) {
            $em->persist($receivedEmailChange);
            unset($this->receivedEmailChanges[$i]);
            $i++;
        }

        if ($i > 0) {
            $em->flush();
        }
    }

问题是在postFlush方法上调用$ entityManager-> flush()会在infinte循环中结束并出现此错误:

  

PHP致命错误:达到了'256'的最大函数嵌套级别,   中止!在   /var/www/sellbytel/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php   在第187行

所以我的问题是:如果可能的话,如何从EventListener中保存数据库中的更改?如果没有,是否有解决方法来创建此日志?

谢谢!

1 个答案:

答案 0 :(得分:1)

如果User的用户名或密码字段发生更改,则会将日志写入UserAudit表,其逻辑与数据库触发器相同。您可以根据需要进行调整。

参考:http://www.inanzzz.com/index.php/post/ew0r/logging-field-changes-with-a-trigger-like-event-listener-for-auditing-purposes

<强> services.yml

services:
    application_backend.event_listener.user_entity_audit:
        class: Application\BackendBundle\EventListener\UserEntityAuditListener
        arguments: [ @security.context ]
        tags:
            - { name: doctrine.event_listener, event: preUpdate }
            - { name: doctrine.event_listener, event: postFlush }

<强>监听

namespace Application\BackendBundle\EventListener;

use Application\BackendBundle\Entity\User;
use Application\BackendBundle\Entity\UserAudit;
use Doctrine\ORM\Event\PostFlushEventArgs;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use Symfony\Component\Security\Core\SecurityContextInterface;

class UserEntityAuditListener
{
    private $securityContext;
    private $fields = ['username', 'password'];
    private $audit = [];

    public function __construct(SecurityContextInterface $securityContextInterface)
    {
        $this->securityContext = $securityContextInterface;
    }

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

        if ($entity instanceof User) {
            foreach ($this->fields as $field) {
                if ($args->getOldValue($field) != $args->getNewValue($field)) {
                    $audit = new UserAudit();
                    $audit->setField($field);
                    $audit->setOld($args->getOldValue($field));
                    $audit->setNew($args->getNewValue($field));
                    $audit->setUser($this->securityContext->getToken()->getUsername());

                    $this->audit[] = $audit;
                }
            }
        }
    }

    public function postFlush(PostFlushEventArgs $args)
    {
        if (! empty($this->audit)) {
            $em = $args->getEntityManager();

            foreach ($this->audit as $audit) {
                $em->persist($audit);
            }

            $this->audit = [];
            $em->flush();
        }
    }
}