Doctrine2在Symfony2中preUpdate缺少PreUpdateEventArgs参数

4

Doctrine2文档中关于preUpdate事件的说明说道

这个事件有一个强大的特性,即通过一个PreUpdateEventArgs实例来执行,其中包含对该实体计算出的更改集的引用。这意味着您可以访问该实体已更改的所有字段及其旧值和新值。

听起来很有用!那我该怎么做:

/**
 * Acme\TestBundle\Entity\Article
 *
 * @ORM\Entity
 * @ORM\HasLifecycleCallbacks
 */
class Article
{
    // ...

    /**
     * @ORM\PreUpdate
     */
    public function preUpdate(\Doctrine\ORM\Event\PreUpdateEventArgs $eventArgs)
    {
        if ( /* use $eventArgs here */ )

            $this->updatedAt = new \DateTime();
    }

    // ...
}

但没有运气 - 没有传递参数:

Catchable Fatal Error: 参数 1 必须是
Doctrine\ORM\Event\PreUpdateEventArgs 的实例,未给出,调用了该参数的方法:
Acme\TestBundle\Entity\Article::preUpdate(),这个方法在
...\vendor\doctrine\lib\Doctrine\ORM\Mapping\ClassMetadataInfo.php 的第 1540 行被调用,在 ...\src\Acme\TestBundle\Entity\Article.php 的第 163 行被定义。

我猜 Symfony2 中必须以其他方式实现。我该怎么做?


1
我曾经遇到过类似的问题,经过4个小时的研究,我发现 Doctrine 从版本 2.4 开始在生命周期注释方法中传递 LifecycleEventArgs。因此,请检查您的依赖项。这是原始问题的链接:http://www.doctrine-project.org/jira/browse/DDC-2186 - Dimitry K
2个回答

17

你应该记住,使用PreUpdateEventArgs实例,你可以访问对于这个实体已经发生改变的所有字段及其旧值和新值。但是在你的示例中updatedAt将不会位于该更改集中。如果你尝试设置updatedAt的值,你应该会得到错误: "Field 'updatedAt' is not a valid field of the entity"。因此,你可以更改updateAt字段,然后使用UnitOfWork:recomputeSingleEntityChangeSet。例如:

public function preUpdate(PreUpdateEventArgs $args)
{
    $entity = $args->getEntity();
    $em = $args->getEntityManager();
    $uow = $em->getUnitOfWork();

    if ($entity instanceof Product)
    {
        $entity->setDiscount(123);

        $uow->recomputeSingleEntityChangeSet(
            $em->getClassMetadata("XXXBundle:Product"),
            $entity
         );
    }
}

在我的博客文章中有更多的代码示例(俄语):在preUpdate事件中更新Doctrine实体


当我尝试使用这种方法并尝试持久化一个新实体时,我会收到Doctrine错误消息:“实体<classname>未被管理。如果从数据库中获取或通过EntityManager#persist注册为新实体,则实体将被管理。”是否有办法使其与新实体一起工作? - Dave Lancea
@DaveLancea 你可以尝试使用 prePersist 事件。它会在新实体被持久化到数据库之前被调用。请参考:Doctrine 文档: "Events - prePersist" - flu
顺便说一下,getClassMetadata被标记为仅限内部使用,你有其他方法可以获取它们吗? - Bactisme

4
处理生命周期事件有两种方法。
第一种方法是简单地将您的Doctrine实体中的一个方法注释为@ORM\PreUpdate。此方法将在更新之前调用。第二种方法更加复杂。它涉及到监听器preUpdate。这是一个单独的类,您订阅它以监听特定事件。
请从这里和Symfonycookbook仔细阅读。

我知道那个选项存在,但我认为我也可以通过注解接收参数。在Symfony中,我应该在哪里设置事件监听器?因为如果我必须在控制器内部设置它,我可以立即执行相同的工作,而无需使用事件监听器。或者是我漏掉了什么吗? - Czechnology
1
你无法通过注释获取参数。我更新了我的答案,并提供了Symfony Cookbook链接,该链接解释了如何注册自己的监听器。 - meze
真遗憾,这会让系统变得更加复杂。感谢您的帮助,我将深入研究事件监听器! - Czechnology
1
但如果你仔细想一下就会明白,因为你可以在生命周期回调上使用多个注释(例如@ORM\PreUpdate@ORM\PreFlush),它们都有不同的参数,因此不能合并到一个函数中。 - flu

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