【问题标题】:Saving the getEntityChangeSet() result in Doctrine EventListener在 Doctrine EventListener 中保存 getEntityChangeSet() 结果
【发布时间】:2017-07-10 14:45:05
【问题描述】:

我需要在 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()。

【问题讨论】:

    标签: php symfony doctrine-orm symfony-3.2


    【解决方案1】:

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

    查看this answer,它建议使用event subscriber,然后持久化到日志实体。

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

    postFlush 在 EntityManager#flush() 结束时被调用。 EntityManager#flush() 不能在其侦听器中安全地调用。

    如果您按照该博客文章的路线,最好使用onFlush() 事件,然后在您的persist() 之后拨打您的computeChangeSets() 电话,就像我发布的第一个答案一样。

    你可以找到类似的example here

    【讨论】:

    • 我可以调用 $uow->getEntityChangeSet();在 onFlush 方法里面安全吗?
    • 你应该可以的。
    • 我能够在 onFlush 和 postFlush 事件上做到这一点,因为在创建对象时,因为我不使用 uuid,我在 onFlush 方法中没有 id。我会按照正确的解决方案接受您的回答。谢谢。
    【解决方案2】:

    您最好使用 事件监听器 来处理此类事情。您想要的更像是记录更改的数据库触发器。请参阅下面的示例(经过测试并且工作正常),该示例记录了 UserAudit 实体中的 User 实体更改。出于演示目的,它只监视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();
            }
        }
    }
    

    【讨论】:

    • 我不确定这个答案,因为它在 Doctrine 文档 PostFlush 中提到:“EntityManager#flush() 不能在其侦听器中安全地调用。”
    • 额外的刷新严格用于刷新您的审计实体,而不是原始的。在附加冲洗之前,原始的已经冲洗了。文档中的那个特定句子是肤浅的,简短的。在得出结论之前,您总是可以尝试看看它是如何工作的。示例有效,并且多年来对我来说仍然可以正常工作。
    • 为什么不使用 getEntityChangeSet?
    【解决方案3】:

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

    https://github.com/simplethings/EntityAuditBundle

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

    【讨论】:

      猜你喜欢
      • 2016-03-24
      • 1970-01-01
      • 1970-01-01
      • 2017-03-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-14
      • 1970-01-01
      相关资源
      最近更新 更多