在原则2(和Symfony)中,$unitOfWork->getScheduledCollectionDeletions()的正确用法是什么?

jaxagkaj  于 2023-01-31  发布在  其他
关注(0)|答案(3)|浏览(130)

我正在尝试检测onFlush事件中多对多关系的变化。
如果新的实体被添加到关系中或者关系被更新(总是保留一个元素),我可以使用$unitOfWork->getScheduledCollectionUpdates()检测变化,然后检查getInsertDiff()getDeleteDiff()
当我把所有实体从关系中取出时,问题就来了:* “以前有两个关联实体,但现在没有关联实体。"*
当关系为空时,我可以访问$unitOfWork->getScheduledCollectionDeletions(),但无法知道哪些实体被删除了:

  • 这个集合的getDeleteDiff()没有任何意义。
    *getSnapshot()不告诉我以前存在哪些实体

我如何知道哪些实体从多对多关系中删除?
我已经添加了一个完整实现的要点:除了$uow->getScheduledCollectionDeletions()(第101行)之外,一切正常(可能需要一些优化)
https://gist.github.com/eillarra/5127606

icnyk63a

icnyk63a1#

此问题的原因有两个:

1)当在Doctrine\ORM\PersistentCollection上调用方法clear()时,它将:
1.清除其内部实体集合。
1.在Doctrine\ORM\UnitOfWork上调用scheduleCollectionDeletion()
1.给自己拍一张新快照。
第二个原因是您的集合显示在$uow->getScheduledCollectionDeletions()中(而不是$uow->getScheduledCollectionUpdates()中);第三个原因是您无法确定集合在被清除之前的内容。
2)当使用Symfony 2表单组件时,特别是ChoiceTypeCollectionType类型与选项multiple结合使用时,当应从集合中删除 * 所有 * 实体时,将调用clear()方法。
这是由于此处添加了MergeDoctrineCollectionListener:https://github.com/symfony/symfony/blob/master/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php#L55
这是作为优化完成的:用这种方式清除集合比检查应该从集合中删除哪些实体要快。
我可以想到两个可能的解决方案
1)创建一个fork symfony/symfony,并实现一个选项,以便 * 不 * 添加MergeDoctrineCollectionListener。也许像no_clear这样的东西可以防止添加侦听器。这不会引入BC中断,并会解决您的问题,因为当 * 所有 * 实体应该被删除时,集合的clear()方法不会被调用。
2)重新设计计数器:也许还可以监听OnLoad事件,该事件可以计算从数据库中获取集合时集合中的实体数量,这样,OnFlush监听器就可以使用该数字来了解在清除集合时从集合中删除了多少实体。

eqqqjvef

eqqqjvef2#

我发现如果将'by_reference' => false,选项设置为EntityType形式,则UnitOfWork会检测集合的更改。请参见OnFlush事件时UnitOfWork中的差异状态:
'by_reference' => false

'by_reference' => true

tvmytwxo

tvmytwxo3#

如果最后一项被删除(比如在提交表单时),“getDeleteDiff”有时会变成空的,但实际上,项是存在的,解决方案是从数据库中获取原始数据。在我的示例中,我们使用集合的克隆来实现这一点。因此,原始集合保持不变,一切仍然有效。

public function onFlush(OnFlushEventArgs $args)
{
    $uow = $args->getEntityManager()->getUnitOfWork();
    
    foreach ($uow->getScheduledCollectionDeletions() as $collection) {
        /**
         * "getDeleteDiff" is not reliable, collection->clear on PersistentCollection also clears the original snapshot
         * A reliable way to get removed items is: clone collection, fetch original data
         */
        $removedData = $collection->getDeleteDiff();
        if (!$removedData) {
            $clone = clone $collection;
            $clone->setOwner($collection->getOwner(), $collection->getMapping());
            // This gets the real data from the database into the clone
            $uow->loadCollection($clone);

            // The actual removed items!
            $removedData = $clone->toArray();
        }
    }
}
  • 〉getDeleteDiff()有时为空的原因是表单的“onSubmit”函数调用了PersistentCollection上的-〉clear()函数。通过清除它,原始的“快照”也被清除了(我猜是出于性能原因)。“getDeleteDiff”函数实际上依赖于该快照,但现在它为空。
    关于这个问题,Github上有多个问题:
    https://github.com/doctrine/orm/issues/2272
    https://github.com/doctrine/orm/issues/4173

相关问题