是否有内置方法来获取Doctrine 2实体中的所有已更改/更新的字段

zer*_*kms 76 php doctrine symfony doctrine-orm

让我们假设我检索一个实体$e并用setter修改它的状态:

$e->setFoo('a');
$e->setBar('b');
Run Code Online (Sandbox Code Playgroud)

有没有可能检索已更改的字段数组?

在我的例子的情况下,我想要检索foo => a, bar => b结果

PS:是的,我知道我可以修改所有的访问者并手动实现这个功能,但我正在寻找一些方便的方法来做到这一点

Ocr*_*ius 135

你可以 Doctrine\ORM\EntityManager#getUnitOfWork用来得到一个Doctrine\ORM\UnitOfWork.

然后只需触发变更集计算(仅适用于托管实体)Doctrine\ORM\UnitOfWork#computeChangeSets().

您也可以使用类似的方法,例如,Doctrine\ORM\UnitOfWork#recomputeSingleEntityChangeSet(Doctrine\ORM\ClassMetadata $meta, $entity)如果您确切地知道要检查的内容而不迭代整个对象图.

之后,您可以使用它Doctrine\ORM\UnitOfWork#getEntityChangeSet($entity)来检索对象的所有更改.

把它放在一起:

$entity = $em->find('My\Entity', 1);
$entity->setTitle('Changed Title!');
$uow = $em->getUnitOfWork();
$uow->computeChangeSets(); // do not compute changes if inside a listener
$changeset = $uow->getEntityChangeSet($entity);
Run Code Online (Sandbox Code Playgroud)

注意.如果尝试在preUpdate侦听器中获取更新的字段,请不要重新计算更改集,因为它已经完成.只需调用getEntityChangeSet即可获得对实体所做的所有更改.

  • @Ocramius,它可能不是它的用途,但它无疑是*有用*.如果只有一种方法可以使用Doctrine来计算没有副作用的变化.例如,如果有一个新的方法/类,也许在UOW中,您可以调用以询问一系列更改.但是这不会以任何方式改变/影响实际的持久性循环.那可能吗? (5认同)
  • 下面的评论说,如果你打电话给$ em-> computerChangeSets(),它会破坏你稍后调用的常规$ em-> persist(),因为它看起来不会像任何东西一样被改变.如果是这样,解决方案是什么,我们不打算调用该函数吗? (4认同)
  • 您不应该在UnitOfWork的生命周期事件侦听器之外使用此API. (3认同)
  • 你不应该.这不是ORM的用途.在这种情况下,通过在应用的操作之前和之后保留数据的副本,使用手动差异. (3认同)
  • 使用$ em-> getUnitOfWork()-> getOriginalEntityData($ entity)查看Mohamed Ramrami bellow发布的更好的解决方案 (2认同)

Sla*_*nko 37

对于那些想要使用上述方法检查实体更改的人来说,要注意大的标志.

$uow = $em->getUnitOfWork();
$uow->computeChangeSets();
Run Code Online (Sandbox Code Playgroud)

$uow->computeChangeSets()方法由持久例程在内部使用,使得上述解决方案无法使用.这也是该方法评论中所写的内容:@internal Don't call from the outside.在检查实体的更改后$uow->computeChangeSets(),在方法结束时执行以下代码(每个管理实体):

if ($changeSet) {
    $this->entityChangeSets[$oid]   = $changeSet;
    $this->originalEntityData[$oid] = $actualData;
    $this->entityUpdates[$oid]      = $entity;
}
Run Code Online (Sandbox Code Playgroud)

$actualData数组保存实体属性的当前更改.一旦写入这些内容$this->originalEntityData[$oid],这些尚未持久的更改将被视为实体的原始属性.

后来,当$em->persist($entity)被称为将更改保存到实体,它也涉及到方法$uow->computeChangeSets(),但现在无法找到更改的实体,因为这些尚未持久的变化被认为是实体的原有特性.

  • @Slavik Derevianko所以你有什么建议?只是不要调用`$ uow-> computerChangeSets()`?或者有什么替代方法? (8认同)

Moh*_*ami 29

检查这个公共(而不是内部)功能:

$this->em->getUnitOfWork()->getOriginalEntityData($entity);

来自学说回购:

/**
 * Gets the original data of an entity. The original data is the data that was
 * present at the time the entity was reconstituted from the database.
 *
 * @param object $entity
 *
 * @return array
 */
public function getOriginalEntityData($entity)
Run Code Online (Sandbox Code Playgroud)

您所要做的就是在您的实体中实现一个toArrayserialize函数并进行差异化.像这样的东西:

$originalData = $em->getUnitOfWork()->getOriginalEntityData($entity);
$toArrayEntity = $entity->toArray();
$changes = array_diff_assoc($toArrayEntity, $originalData);
Run Code Online (Sandbox Code Playgroud)

  • 当 Entity 与另一个(可以是 OneToOne)相关时,如何将此应用于这种情况?在这种情况下,当我在 top-lvl 实体上运行 getOriginalEntityData 时,其相关实体的原始数据并不是真正的原始数据而是更新的。 (3认同)

Oma*_*led 7

它将返回更改

$entityManager->getUnitOfWork()->getEntityChangeSet($entity)
Run Code Online (Sandbox Code Playgroud)


man*_*nix 5

您可以使用通知政策跟踪更改.

首先,实现NotifyPropertyChanged接口:

/**
 * @Entity
 * @ChangeTrackingPolicy("NOTIFY")
 */
class MyEntity implements NotifyPropertyChanged
{
    // ...

    private $_listeners = array();

    public function addPropertyChangedListener(PropertyChangedListener $listener)
    {
        $this->_listeners[] = $listener;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,只需在每个更改数据的方法上调用_onPropertyChanged,如下所示:

class MyEntity implements NotifyPropertyChanged
{
    // ...

    protected function _onPropertyChanged($propName, $oldValue, $newValue)
    {
        if ($this->_listeners) {
            foreach ($this->_listeners as $listener) {
                $listener->propertyChanged($this, $propName, $oldValue, $newValue);
            }
        }
    }

    public function setData($data)
    {
        if ($data != $this->data) {
            $this->_onPropertyChanged('data', $this->data, $data);
            $this->data = $data;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 实体内部的监听器?!疯狂!说真的,跟踪策略看起来是一个很好的解决方案,有没有办法在实体之外定义监听器(我正在使用Symfony2 DoctrineBundle). (7认同)