diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 5e340aa7df9..bac9c7ac51b 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -1165,31 +1165,12 @@ public function recomputeSingleEntityChangeSet(ClassMetadata $class, $entity) private function executeInserts(): void { $entities = $this->computeInsertExecutionOrder(); + $eventsToDispatch = []; foreach ($entities as $entity) { - $oid = spl_object_id($entity); - - // Mitigation for GH-10869: - // Users may use postPersist and similar listeners to make entity updates and call - // EM::flush() -> UoW::commit() again, while a transaction is currently running. - // This "somehow" worked pre 2.16, although it was never officially endorsed and/or - // is disputed (and there is no guarantee that the UoW will be able to deal with this, - // does not lose updates etc.). - // https://github.com/doctrine/orm/pull/10900 is a discussion about deprecating this - // kind of reentrance and disallowing it in 3.0. - // - // However, to ease the pain somewhat for users in 2.16, this condition covers that - // a reentrant call into UoW::commit() may have processed pending insertions that we - // had in our computed insertion order. So, after the second (inner) commit() returned - // and the outer one continues, deal with the situation that entities are no longer in - // the set of pending insertions. - if (! isset($this->entityInsertions[$oid])) { - continue; - } - + $oid = spl_object_id($entity); $class = $this->em->getClassMetadata(get_class($entity)); $persister = $this->getEntityPersister($class->name); - $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postPersist); $persister->addInsert($entity); @@ -1216,10 +1197,19 @@ private function executeInserts(): void $this->addToEntityIdentifiersAndEntityMap($class, $oid, $entity); } + $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postPersist); + if ($invoke !== ListenersInvoker::INVOKE_NONE) { - $this->listenersInvoker->invoke($class, Events::postPersist, $entity, new PostPersistEventArgs($entity, $this->em), $invoke); + $eventsToDispatch[] = ['class' => $class, 'entity' => $entity, 'invoke' => $invoke]; } } + + // Mitigation for GH-10869: + // Defer dispatching `postPersist` events to until all entities have been inserted and there + // are no pending insertions left. + foreach ($eventsToDispatch as $event) { + $this->listenersInvoker->invoke($event['class'], Events::postPersist, $event['entity'], new PostPersistEventArgs($event['entity'], $this->em), $event['invoke']); + } } /**