diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 64ca810e78b..b4e1a8ab451 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -3048,6 +3048,7 @@ public function createEntity($className, array $data, &$hints = []) // We are negating the condition here. Other cases will assume it is valid! case $hints['fetchMode'][$class->name][$field] !== ClassMetadata::FETCH_EAGER: $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $normalizedAssociatedId); + $this->registerManaged($newValue, $associatedId, []); break; // Deferred eager load only works for single identifier classes @@ -3056,6 +3057,7 @@ public function createEntity($className, array $data, &$hints = []) $this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($normalizedAssociatedId); $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $normalizedAssociatedId); + $this->registerManaged($newValue, $associatedId, []); break; default: @@ -3063,13 +3065,6 @@ public function createEntity($className, array $data, &$hints = []) $newValue = $this->em->find($assoc['targetEntity'], $normalizedAssociatedId); break; } - - if ($newValue === null) { - break; - } - - $this->registerManaged($newValue, $associatedId, []); - break; } $this->originalEntityData[$oid][$field] = $newValue; diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/GH10880Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/GH10880Test.php new file mode 100644 index 00000000000..a9c53c67321 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/GH10880Test.php @@ -0,0 +1,117 @@ +setUpEntitySchema([ + GH10880BaseProcess::class, + GH10880Process::class, + GH10880ProcessOwner::class, + ]); + } + + public function testProcessShouldBeUpdated(): void + { + $process = new GH10880Process(); + $process->description = 'first value'; + + $owner = new GH10880ProcessOwner(); + $owner->process = $process; + + $this->_em->persist($process); + $this->_em->persist($owner); + $this->_em->flush(); + $this->_em->clear(); + + $ownerLoaded = $this->_em->getRepository(GH10880ProcessOwner::class)->find($owner->id); + $ownerLoaded->process->description = 'other description'; + + $queryLog = $this->getQueryLog(); + $queryLog->reset()->enable(); + $this->_em->flush(); + + $this->removeTransactionCommandsFromQueryLog(); + + self::assertCount(1, $queryLog->queries); + $query = reset($queryLog->queries); + self::assertSame('UPDATE GH10880BaseProcess SET description = ? WHERE id = ?', $query['sql']); + } + + private function removeTransactionCommandsFromQueryLog(): void + { + $log = $this->getQueryLog(); + + foreach ($log->queries as $key => $entry) { + if ($entry['sql'] === '"START TRANSACTION"' || $entry['sql'] === '"COMMIT"') { + unset($log->queries[$key]); + } + } + } +} + +/** + * @ORM\Entity + */ +class GH10880ProcessOwner +{ + /** + * @ORM\Id + * @ORM\GeneratedValue + * @ORM\Column(type="integer") + * + * @var int + */ + public $id; + + /** + * fetch=EAGER is important to reach the part of \Doctrine\ORM\UnitOfWork::createEntity() + * that is important for this regression test + * + * @ORM\ManyToOne(targetEntity="GH10880Process", fetch="EAGER") + * + * @var GH10880Process + */ + public $process; +} + +/** + * @ORM\Entity() + * @ORM\InheritanceType("SINGLE_TABLE") + * @ORM\DiscriminatorColumn(name="type", type="string") + * @ORM\DiscriminatorMap({"process" = "GH10880Process"}) + */ +abstract class GH10880BaseProcess +{ + /** + * @ORM\Id + * @ORM\GeneratedValue + * @ORM\Column(type="integer") + * + * @var int + */ + public $id; + + /** + * @ORM\Column(type="text") + * + * @var string + */ + public $description; +} + +/** + * @ORM\Entity + */ +class GH10880Process extends GH10880BaseProcess +{ +}