Doctrine 2.1 在 preUpdate lifeCycleCallback 中持久化实体

Posted

技术标签:

【中文标题】Doctrine 2.1 在 preUpdate lifeCycleCallback 中持久化实体【英文标题】:Doctrine 2.1 Persist entity in preUpdate lifeCycleCallback 【发布时间】:2011-10-25 10:07:41 【问题描述】:

我正在努力解决以下问题,在一个实体类中,我有一个 preUpdate lifeCycleCallback,它必须在刷新 auditTrail 的更改之前保留一个新实体。

在 preRemove 和 prePersist 中,这可以完美运行,但在 preUpdate 中什么也没发生。如果我自己调用flush,它会进入一个递归循环。

根据教义用户的 Google 组将其放入 onFlush 应该是一个选项,但在这种情况下,我无法访问实体的旧值以将这些旧值保存在新的其他实体中以进行审计。

我要归档的一些小例子:

<?php
/**
 * @Entity
 * @HasLifeCycleCallbacks
 */
class someEntity 
    ... annotations ...


    /**
     * @PreUpdate
     */
    public function addAuditTrail() 
        $em = \Zend_Registry::get('doctrine')->getEntityManager();

        $entity = new AuditTrail();
        $entity->action = 'update';
        $entity->someField = $this->someField;

        $em->persist($entity); //this just doesn't do anything :-(
    

?>

这不是真正的代码,只是为了说明我想要的东西。我也尝试过这样的事情:

$em->getUnitOfWork()->computeChangeSet($em->getClassMetaData(get_class($entity)), $entity);

应该根据这个主题工作:http://groups.google.com/group/doctrine-user/browse_thread/thread/bd9195f04857dcd4

如果我再次调用刷新,但这会导致 Apache 由于一些无限循环而崩溃。

有谁对我有想法吗?谢谢!

【问题讨论】:

【参考方案1】:

您永远不应该在您的实体中使用实体管理器。如果您想添加审计跟踪,您应该将“SomeEntity”实体映射到“AuditTrail”实体并执行类似

/**
 * @PreUpdate
 */
public function addAuditTrail() 
    $entity = new AuditTrail();
    $entity->action = 'update';
    $entity->someField = $this->someField;

    $this->autitTrail->add($entity);

如果你在映射上设置了级联选项,当你持久化“SomeEntity”时它会被持久化。

【讨论】:

为什么不应该在我的实体中使用实体管理器?如果您将 AuditEntity 映射到 SomeEntity 它将在数据库中关联,这不是我想要复制我的实体并添加一些其他字段,如“action”= update 等。这个 AuditEntity 只是一个例子,因为我使用审计:github.com/keesschepers/EntityAudit 我会使用事件管理器。想看看你的代码,但还没有时间。 我终于(一年前!)使用事件管理器 (EventSubscriber) 在全球范围内完成这些任务。我已将其包含在我的框架中:github.com/php-pike/Pike(查看 EntityAudit 部分)谢谢! 另见***.com/questions/16904462/…【参考方案2】:

我在 EventListener 的 preUpdate 方法中遇到了同样的问题。我通过将新实体存储在属性中并将新的 persist() 和 flush() 调用移动到 postUpdate 方法解决了这个问题。

class someEntity 
... annotations ...

protected $store;

/**
 * @PreUpdate
 */
public function addAuditTrail() 
    //$em = \Zend_Registry::get('doctrine')->getEntityManager();

    $entity = new AuditTrail();
    $entity->action = 'update';
    $entity->someField = $this->someField;

    // replaces $em->persist($entity); 
    $this->store = $entity;


/**
 * @PostUpdate
 */
public function saveAuditTrail() 
    $em = \Zend_Registry::get('doctrine')->getEntityManager();
    $em->persist($this->store); 
    $em->flush();

【讨论】:

我知道它看起来有点“hack like”,但我看不到这个用例的“官方”解决方案,我也使用过这种方法 不幸的是,Doctrine 不支持在生命周期回调中调用flush(),这似乎在较新的 Doctrine 版本中不起作用;我收到此错误:doctrine-project.org/jira/browse/DDC-3218. 查看此链接了解如何正确操作:***.com/questions/16904462/…。我希望有一种方法可以在实体类中做到这一点,而不是必须创建一个单独的全局类......【参考方案3】:

entitymanager->persist() 在 preUpdate 方法中不起作用。 取而代之的是,您可以将 AuditTrail 数据保存到会话中,并在刷新“SomeEntity”之后,从会话中获取数据并执行 entitymanager->persist(...) 和 entitymanager->flush()

【讨论】:

以上是关于Doctrine 2.1 在 preUpdate lifeCycleCallback 中持久化实体的主要内容,如果未能解决你的问题,请参考以下文章

在 Symfony 2.1 中为 preUpdate 调用添加额外的持久化调用

Doctrine 2.1 性能问题

Doctrine 2.1 @Column 注释中“选项”的语法是啥?

Doctrine 2.1性能问题

Hibernate @PreUpdate:检查已更改的内容

如何使用 Symfony 2.1 从内部控制器获取 Doctrine 实体的实体管理器