Symfony 2 / Doctrine 2:获取PersistentCollection的更改

6
我正在开发一个应用程序,用户可以编辑一些数据,然后会出现一个屏幕,用户可以确认(并对其进行评论)他的编辑。
在确认表单中,我显示了对实体所做的更改。这适用于“普通”字段。以下是可用于检查单个字段的一些代码:
// create $form
// bind $form

if ($form->isValid() {
    $data = $form->getData();
    // example, get changes of a "normal" field
    if ($data['color'] != $entity->getColor()) {
        // do something with changes
    }
}

但我无法对关系进行相同的操作(例如与用户的多对多关系):

    if ($data['users'] != $entity->getUsers()

这段代码无法正常工作,因为$data['users']和$entity->getUsers()指向同一个持久化集合。可以调用此函数来检查是否有更改:

    if ($data['users']->isDirty())

但是,无法看到所做的更改。

上述方法的第二个问题是,如果从持久化集合中删除了所有项目,则Doctrine不会将其标记为“已更改”(isDirty()= true),因此我无法捕获用户在表单中删除实体中的所有“用户”的特定更改。

请注意,代码都可以正常工作,我唯一的问题是无法查看/处理确认步骤中所做的更改。


在绑定之前检索$entity->getUsers()?同时检查并查看您的表单类型是否通过引用添加了该字段。 - Lighthart
@Lighthart 那是个好主意(通过引用)!我会检查并回复你的... - mogoman
@Lighthart不起作用,因为按引用是针对集合而不是字段实体的。 - mogoman
3个回答

8

Doctrine\ORM\PersistentCollection有内部API(公共)方法getSnapshotgetDeleteDiffgetInsertDiff,可以在Doctrine\ORM\UnitOfWork的生命周期事件期间使用。例如,在onFlush期间,您可以检查持久集合的插入差异。


谢谢+1,但如果整个集合被清除了,这些方法就不起作用了(isDirty()也是PersistentCollection的一个方法),这是我的主要问题。 - mogoman
在计算出变更集之后(在flush期间),您应该使用这些方法。 - Ocramius
问题在于我需要展示更改供用户接受/取消,这意味着我不能调用onFlush()。 - mogoman

6
这样解决它:
1)要获取直接应用于实体的更改,请使用以下方法:
// create form
// bind form
// form isValid()

$uow = $em->getUnitOfWork();
$uow->computeChangeSets();
$changeset = $uow->getEntityChangeSet($entity);
print_r($changeset);

2a) 要更改关系,请使用Lighthart上面的答案:
$oldUsers = $entity->getUsers()->toArray();
// bind form
// form isValid
$newUsers = $entity->getUsers()->toArray();
// compare $oldUsers and $newUsers

2b) 使用这些方法在持久化集合中查找插入/删除操作:
$newUsers = $entity->getUsers();
$inserted = $newUsers->getDeleteDiff();
$deleted  = $newUsers->getInsertDiff();

(2b) 的唯一问题是,如果全部用户都被删除并且没有添加,则 getDeleteDiff() 为空,这似乎是一个 Doctrine 的 bug/特殊情况。

我正在思考该如何处理这个问题,不知道在哪里放置这个逻辑。我的第一反应是控制器动作,但每个人都说要保持你的控制器精简。我想我正在寻找一个事件监听器——可以在Doctrine事件系统或Zend Framework 2的事件系统中获取——以便我可以捕捉到这个事件。当实体关联发生某些事情时,我需要清除缓存。 - David
先生,这太棒了! - Stepan Yudin
在2b中,我没有遇到你的问题,getDeleteDiff()即使所有集合项都被删除,也会返回数组。 - Stepan Yudin
是的,我猜自从我第一次发帖问这个问题以来,Doctrine 已经发布了相当多的版本 :-) - mogoman
非常感谢!提示“如果删除了所有用户并且没有添加任何用户,则getDeleteDiff()为空,这似乎是Doctrine的错误”非常有帮助! - Armin

0

在绑定之前将原始集合存储在变量中,然后比较绑定后的新集合。PHP有相当多的数组比较函数,并且可以通过$collection->toArray()轻松地将集合转换为本机数组。

例如:

// create form
$oldusers=$entity->getUsers()->toArray();
// bind form
if ($form->isValid() {
    $data = $form->getData();
    if ($data['users'] != $oldusers) {
        // do something with changes
    }
}

无法正常工作,绑定 $oldusers 后也会更新更改。这是因为 $oldusers 引用了相同的集合。 - mogoman
只需将其转换为数组。 数组和对象绝对不可能是同一内存位置。 - Lighthart
谢谢。最终我使用了toArray()。我也附上了我的最终解决方案,以便有人想评论/改进它。 - mogoman
这句话应该是 $oldusers=$entity->getUsers()->toArray();,我被禁止修改/修复一个字符 :D - Chadwick Meyer
Fixed per CM's comment - Lighthart

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