在onFlush侦听器中重新计算实体变更集

时间:2013-05-16 21:26:42

标签: doctrine-orm changeset event-listener

考虑以下架构:

[Work]
id
tags ManyToMany(targetEntity="Tag", inversedBy="works", cascade={"persist"})

[Tag]
id
works_count
works ManyToMany(targetEntity="Work", mappedBy="tags")

works_countTag::works的计数器缓存。

我在工作上有一个onFlush监听器,用于检查Work::tags是否已更改,并更新每个标记“works_count

public function onFlush(OnFlushEventArgs $args)
{
    foreach ($uow->getScheduledEntityUpdates() as $work) {
        $changedTags = /* update relevant tags and return all the changed ones */

        $metadata = $em->getClassMetadata('Acme\Entity\Tag');

        foreach ($changedTags as $tag) {
            $uow->recomputeSingleEntityChangeSet($metadata, $tag);
        }
    }
}

现在,如果我阅读更新代码的更改集,works_count的更改会正确显示,则不会在数据库中更新。

如果我将recomputeSingleEntityChangeSet()替换为computeChangeSet(),那么一切都按预期工作并且数据库已更新,但computeChangeSet()上有一个@internal Don't call from the outside.注释,所以我不是确定后果是什么..

互联网上的每个消息来源都说使用recomputeSingleEntityChangeSet,为什么在这种情况下它不起作用?

P.S 标签由EntityManager管理($em->contains($tag)返回true)

4 个答案:

答案 0 :(得分:3)

此问题与UnitOfWork中的错误有关,最后在2014年9月11日发布Doctrine ORM 2.4.3时已修复。有关详细信息,请参阅DDC-2996

答案 1 :(得分:2)

似乎Doctrine 2.2可以合并变更集或生成新的变更集,但它需要知道哪些变更集。如果你弄错了,它将替换现有的更改集或根本不做任何事情。我很想知道是否有比这更好的选择,或者这是否正确。

    if($uow->getEntityChangeSet($entity)) {
        /** If the entity has pending changes, we need to recompute/merge. */
        $uow->recomputeSingleEntityChangeSet($meta, $contact);
    } else {
        /** If there are no changes, we compute from scratch? */
        $uow->computeChangeSet($meta, $entity);
    }

答案 2 :(得分:1)

在doctrine 2.4.1中,仅当您在事件侦听器中更改标记并且UOW包含标记ChangeSet(在事件侦听器之外发生的更改)时,才使用recomputeSingleEntityChangeSet。基本上,recomputeSingleEntityChangeSet是一个合并实体的ChangeSet的函数。

来自该功能的Doc 传递的实体必须是管理实体。如果实体已经有一个更改集,因为在提交周期中调用了此方法,则会添加更改集,以此方法中检测到的更改为准。

注意:您需要确保UOW已经有实体的ChangeSet,否则它将不会合并。

答案 3 :(得分:0)

对于将来的读者,不惜一切代价,请尝试避免 听众。这些几乎无法测试,您的领域不应该依赖魔术。考虑OP的测试用例如何在没有教义事件的情况下实现相同的目标:

工作课程:

public function addTag(Tag $tag): void
{
    if (!$this->tags->contains($tag)) {
        $this->tags->add($tag);
        $tag->addWork($this);
    }
}

标记类:

public function addWork(Work $work): void
{
    if (!$this->works->contains($work)) {
        $work->addTag($this);
        $this->works->add($work);
        $this->worksCount = count($this->works);
    }
}

TagTest 类:

public function testItUpdatesWorksCountWhenWorkIsAdded()
{
    $tag = new Tag();

    $tag->addWork(new Work());
    $tag->addWork(new Work());

    $this->assertSame(2, $tag->getWorkCount());
}

public function testItDoesNotUpdateWorksCountIfWorkIsAlreadyInCollection()
{
    $tag = new Tag();
    $work = new Work();
    $tag->addWork($work);

    $tag->addWork($work);

    $this->assertSame(1, $tag->getWorkCount());
}