将getEntityChangeSet()结果保存在Doctrine EventListener中

时间:2017-07-10 14:45:06

标签: php symfony doctrine-orm symfony-3.2

我需要在API中为实体上的用户操作创建更新日志。

例如:

用户更新实体许可方我需要捕获更改并将它们保存在不同表中的数据库中。

我能够使用Doctrine Event Listener进行第一部分

class ChangelogEventListener
{
   public function preUpdate($obj, PreUpdateEventArgs $eventArgs)
   {
       if ($obj instanceof LoggableInterface) {
            dump($eventArgs->getEntityChangeSet());
       }
   }
}

标记实体事件监听器

/**
 * @ORM\EntityListeners(value={"AppBundle\EventSubscriber\Changelogger\ChangelogEventListener"})
 */
class Licensor implements LoggableInterface

但我不确定它是否可能,以及在preUpdate事件中访问ORM实体管理器是否有意义。

如果不是,那么正确的做法是什么?

我尝试使用Symfony的EventListener而不是Doctrine,但后来我无法访问getEntityChangeSet(

3 个答案:

答案 0 :(得分:2)

最好使用事件监听器来做这件事。您想要的更像是记录更改的数据库触发器。请参阅下面的示例(已测试且工作正常),这会在User实体中记录UserAudit实体更改。出于演示目的,它只会监视usernamepassword字段,但您可以根据需要进行修改。

注意:如果您想要实体监听器,请查看this example

<强> 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 }

<强> UserEntityAuditListener

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();
        }
    }
}

答案 1 :(得分:1)

结帐Doctrine events,特别是preUpdate event。此事件是最严格的,但您可以访问已更改的所有字段及其旧/新值。您可以更改此处正在更新的实体的值,除非它是关联的实体。

查看this answer,建议使用event subscriber,然后保留给日志记录实体。

还有this blog post使用preUpdate事件将一堆更改集保存到内部侦听器类,然后postFlush它会保留任何正在更改的实体,并调用{ {1}}再次。但是,我会推荐这个,Doctrine documentation explicitly states

  

在EntityManager #flush()结束时调用postFlush。   无法在侦听器内安全地调用EntityManager#flush()。

如果您选择了该博文的路线,最好使用flush活动,然后在onFlush()之后进行computeChangeSets()来电,就像我发布的第一个回答一样

您可以找到类似的example here

答案 2 :(得分:0)

检查此repo的代码或只使用该捆绑包中的某人。

https://github.com/simplethings/EntityAuditBundle

https://github.com/DATA-DOG/DataDogAuditBundle