onFlush中的预定实体是不同的实例

时间:2016-12-12 13:50:27

标签: php symfony orm doctrine-orm

我在\Doctrine\ORM\UnitOfWork::getScheduledEntityDeletions事件

中使用的onFlush有一个奇怪的问题
foreach ($unitOfWork->getScheduledEntityDeletions() as $entity) {
    if ($entity instanceof PollVote) {
        $arr = $entity->getAnswer()->getVotes()->toArray();

        dump($arr);
        dump($entity);

        dump(in_array($entity, $arr, true));
        dump(in_array($entity, $arr));
    }
}

结果如下:

dump() output

因此我们看到该对象指向的是与原始实例不同的实例,因此当使用棒比较(AKA in_array)时,===不再产生预期结果。此外,\DateTime对象指向不同的实例。

我找到的唯一可能的解释是以下(source):

  

每当从数据库中获取对象时,Doctrine将保留UnitOfWork内所有属性和关联的副本。因为PHP语言中的变量受“写入时复制”的影响,所以只从数据库中读取对象的PHP请求的内存使用情况与Doctrine不保留此变量副本的内存使用情况相同。只有当您开始更改变量时,PHP才会在内部创建新变量,从而消耗新内存。

然而,我没有改变任何东西(即使created字段保持原样)。在实体上执行的唯一操作是:

  1. \Doctrine\ORM\EntityRepository::findBy(从DB获取)
  2. \Doctrine\Common\Persistence\ObjectManager::remove(安排删除)
  3. $em->flush();(触发与DB的同步)
  4. 这让我想到(我可能错了),该学说的变化跟踪方法与我遇到的问题无关。这让我想到了以下问题:

    • 是什么原因引起的?
    • 如何可靠地检查计划删除的实体是否在集合中(\Doctrine\Common\Collections\Collection::contains使用in_array进行严格比较)或者集合中的哪些项目计划删除?

2 个答案:

答案 0 :(得分:1)

问题在于,当您告诉doctrine删除实体时,它将从身份地图(here)中删除:

<?php

public function scheduleForDelete($entity)
{
    $oid = spl_object_hash($entity);

    // ....

    $this->removeFromIdentityMap($entity);

    // ...

    if ( ! isset($this->entityDeletions[$oid])) {
        $this->entityDeletions[$oid] = $entity;
        $this->entityStates[$oid]    = self::STATE_REMOVED;
    }
}

当你执行$entity->getAnswer()->getVotes()时,它会执行以下操作:

  1. 加载数据库中的所有投票
  2. 对于每次投票,检查它是否在身份图中,使用旧的
  3. 如果它不在身份地图中,请创建新对象
  4. 尝试在删除实体之前调用$entity->getAnswer()->getVotes()。如果问题消失,那我就是对的。当然,我不建议将此黑客作为解决方案,只是为了确保我们了解幕后发生的事情。

    UPD 而不是$entity->getAnswer()->getVotes()你应该为所有选票做foreach,因为延迟加载。如果你只是调用$entity->getAnswer()->getVotes(),Doctrine可能不会做任何事情,只有当你开始迭代它们时才会加载它们。

答案 1 :(得分:0)

来自doc

  

如果您调用EntityManager并要求具有特定ID两次的实体,它将返回相同的实例

所以调用两次findOneBy(['id' => 12])应该会产生两个完全相同的实例。

所以这一切都取决于Doctrine如何检索这两个实例。 在我看来,你在$ arr中得到的是来自Answer实体中$ votes的一对多关联,这导致ORM单独查询(可能是id IN (12))。

您可以尝试的是将此关联声明为EAGER(fetch="EAGER"),它可能会强制ORM进行特定查询并将其保留在缓存中,以便第二次获取它时,相同实例返回?

您可以查看日志并在此处发布吗?它可能表示一些有趣或至少与进一步调查相关的内容。