Skip to content

Commit b0d8fec

Browse files
mpdudesylfabre
andcommitted
Make it possible to have non-NULLable self-referencing associations when using application-provided IDs
The change makes the `BasicEntityPersister` not schedule an extra update in the case of an entity referencing itself and having an application-provided ID (the "NONE" generator strategy). While it looks like a special corner case, the INSERTion of a NULL value plus the extra update require the self-referencing column to be defined as NULLable. This is only an issue for the self-referencing entity case. All other associations work naturally. Fixes #7877, closes #7882. Co-authored-by: Sylvain Fabre <sylvain.fabre@assoconnect.com>
1 parent da0998c commit b0d8fec

File tree

2 files changed

+71
-1
lines changed

2 files changed

+71
-1
lines changed

lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php

+11-1
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,17 @@ protected function prepareUpdateData($entity, bool $isInsert = false)
683683
if ($newVal !== null) {
684684
$oid = spl_object_id($newVal);
685685

686-
if (isset($this->queuedInserts[$oid]) || $uow->isScheduledForInsert($newVal)) {
686+
if ($newVal === $entity && $this->class->isIdentifierNatural()) {
687+
// When the associated entity is in fact the entity itself (= we have a
688+
// self-referencing entity), and the ID has already been provided by the
689+
// application, then we can INSERT all the data right away and need not
690+
// schedule the extra update. Due to the "references itself" condition,
691+
// this looks like a very special case. But, in fact, this is the
692+
// corner case that makes it possible to define such self-referencing
693+
// columns as not-NULLable when using application-side ID generators.
694+
695+
// do nothing
696+
} elseif (isset($this->queuedInserts[$oid]) || $uow->isScheduledForInsert($newVal)) {
687697
// The associated entity $newVal is not yet persisted, so we must
688698
// set $newVal = null, in order to insert a null value and schedule an
689699
// extra update on the UnitOfWork.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\Tests\ORM\Functional;
6+
7+
use Doctrine\ORM\Mapping as ORM;
8+
use Doctrine\Tests\OrmFunctionalTestCase;
9+
10+
use function uniqid;
11+
12+
/**
13+
* @group GH7877
14+
*/
15+
class GH7877Test extends OrmFunctionalTestCase
16+
{
17+
protected function setUp(): void
18+
{
19+
parent::setUp();
20+
21+
$this->createSchemaForModels(
22+
GH7877ApplicationGeneratedIdEntity::class
23+
);
24+
}
25+
26+
public function testSelfReferenceWithApplicationGeneratedIdMayBeNotNullable(): void
27+
{
28+
$entity = new GH7877ApplicationGeneratedIdEntity();
29+
$entity->id = uniqid();
30+
$entity->parent = $entity;
31+
32+
$this->expectNotToPerformAssertions();
33+
34+
$this->_em->persist($entity);
35+
$this->_em->flush();
36+
}
37+
}
38+
39+
/**
40+
* @ORM\Entity
41+
*/
42+
class GH7877ApplicationGeneratedIdEntity
43+
{
44+
/**
45+
* @ORM\Id
46+
* @ORM\Column(type="string")
47+
* @ORM\GeneratedValue(strategy="NONE")
48+
*
49+
* @var string
50+
*/
51+
public $id;
52+
53+
/**
54+
* @ORM\ManyToOne(targetEntity="GH7877ApplicationGeneratedIdEntity")
55+
* @ORM\JoinColumn(name="parent_id", referencedColumnName="id", nullable=false)
56+
*
57+
* @var self
58+
*/
59+
public $parent;
60+
}

0 commit comments

Comments
 (0)