diff --git a/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php index 144f340d000..fc74b69335f 100644 --- a/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php @@ -683,7 +683,17 @@ protected function prepareUpdateData($entity, bool $isInsert = false) if ($newVal !== null) { $oid = spl_object_id($newVal); - if (isset($this->queuedInserts[$oid]) || $uow->isScheduledForInsert($newVal)) { + if ($newVal === $entity && $this->class->isIdentifierNatural()) { + // When the associated entity is in fact the entity itself (= we have a + // self-referencing entity), and the ID has already been provided by the + // application, then we can INSERT all the data right away and need not + // schedule the extra update. Due to the "references itself" condition, + // this looks like a very special case. But, in fact, this is the + // corner case that makes it possible to define such self-referencing + // columns as not-NULLable when using application-side ID generators. + + // do nothing + } elseif (isset($this->queuedInserts[$oid]) || $uow->isScheduledForInsert($newVal)) { // The associated entity $newVal is not yet persisted, so we must // set $newVal = null, in order to insert a null value and schedule an // extra update on the UnitOfWork. diff --git a/tests/Doctrine/Tests/ORM/Functional/GH7877Test.php b/tests/Doctrine/Tests/ORM/Functional/GH7877Test.php new file mode 100644 index 00000000000..525211eb0cc --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/GH7877Test.php @@ -0,0 +1,60 @@ +createSchemaForModels( + GH7877ApplicationGeneratedIdEntity::class + ); + } + + public function testSelfReferenceWithApplicationGeneratedIdMayBeNotNullable(): void + { + $entity = new GH7877ApplicationGeneratedIdEntity(); + $entity->id = uniqid(); + $entity->parent = $entity; + + $this->expectNotToPerformAssertions(); + + $this->_em->persist($entity); + $this->_em->flush(); + } +} + +/** + * @ORM\Entity + */ +class GH7877ApplicationGeneratedIdEntity +{ + /** + * @ORM\Id + * @ORM\Column(type="string") + * @ORM\GeneratedValue(strategy="NONE") + * + * @var string + */ + public $id; + + /** + * @ORM\ManyToOne(targetEntity="GH7877ApplicationGeneratedIdEntity") + * @ORM\JoinColumn(name="parent_id", referencedColumnName="id", nullable=false) + * + * @var self + */ + public $parent; +}