The scheduled entity in onFlush is a different instance

I have a strange problem with \Doctrine\ORM\UnitOfWork::getScheduledEntityDeletions used inside the onFlush event

 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)); } } 

And here is the result:

dump () output

So, we see that the object points to a different instance than the original, so in_array no longer gives the expected results when using comparison with a stick (AKA === ). In addition, the \DateTime object points to another instance.

The only possible explanation I found is the following ( source ):

Whenever you retrieve an object from the database, Doctrine will keep a copy of all properties and associations inside UnitOfWork. Since variables in the PHP language obey "copy to write", the memory usage of a PHP request that reads only objects from the database is the same as if Doctrine did not save this variable. Only if you start changing variables will PHP create new variables inside that consume new memory.

However, I did not change anything (even the created field is stored as is). The only operations that were formed in essence are:

  • \Doctrine\ORM\EntityRepository::findBy ( \Doctrine\ORM\EntityRepository::findBy from database)
  • \Doctrine\Common\Persistence\ObjectManager::remove (planning to remove)
  • $em->flush(); (start synchronization with the database)

This makes me think (maybe I'm wrong) that the change tracking method in Doctrine has nothing to do with the problem I am facing. This leads me to the following questions:

  • What causes this?
  • How to reliably verify that the object planned for deletion is inside the collection ( \Doctrine\Common\Collections\Collection::contains uses in_array with strict matching), or which elements in the collection are planned for deletion?
+6
source share
2 answers

The problem is that when you tell the doctrine to delete an object, it is removed from the identification card ( 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; } } 

And when you do $entity->getAnswer()->getVotes() , it does the following:

  • Download all votes from the database
  • For each vote, check if it is in the identification card, use the old
  • If this is not an identity card, create a new object.

Try calling $entity->getAnswer()->getVotes() before deleting the object. If the problem goes away, then I'm right. Of course, I would not offer this hack as a solution, just to make sure that we understand what is happening under the hood.

UPD, instead of $entity->getAnswer()->getVotes() you should probably do foreach for all votes, due to lazy loading. If you simply call $entity->getAnswer()->getVotes() , Doctrine will probably not do anything, and only load them when you start iterating over them.

+1
source

From doc :

If you call EntityManager and twice query an entity with a specific identifier, it returns the same instance

Thus, a call to twice findOneBy(['id' => 12]) should result in two exactly the same instances.

So it all depends on how both instances are retrieved by Doctrine. In my opinion, the one you get in $ arr is associated with the one-to-many association of $ votes in the Response object, as a result of which ORM executes a separate request (possibly id IN (12) ).

Something you could try is to declare this association as EAGER ( fetch="EAGER" ), this may cause ORM to make a specific request and store it in the cache so that the second time you receive it, the same instance is back ?

Could you look at the magazines and post them here? This may indicate that it is interesting, or at least relevant to further research.

0
source

Source: https://habr.com/ru/post/1013092/


All Articles