-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This change makes the
BasicEntityPersister
not schedule extra updat…
…es for association-target entities that use application-provided IDs (the "NONE" generator strategy). For this generator strategy, we may assume that IDs are already present at the time the insert is executed. The benefit is that now the join column can be defined as not-NULLable, since the ORM no longer uses temporary NULL values. However, care must be taken: Extra updates are not only about the question of having IDs for the associated entities available, but also about having those entities in the database already (to satisfy foreign key constaints). The latter will become easier with #10547, since the UoW will take care of that detail when computing the commit order. But without that detail, we might break use cases that currently work – that's cases where the extra update gives us the extra time to insert the referred-to entity first. Fixes #7877, closes #7882. Co-authored-by: Sylvain Fabre <[email protected]>
- Loading branch information
Showing
2 changed files
with
145 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Doctrine\Tests\ORM\Functional; | ||
|
||
use Doctrine\ORM\Mapping as ORM; | ||
use Doctrine\Tests\OrmFunctionalTestCase; | ||
|
||
use function uniqid; | ||
|
||
/** | ||
* @group GH7877 | ||
*/ | ||
class GH7877Test extends OrmFunctionalTestCase | ||
{ | ||
protected function setUp(): void | ||
{ | ||
parent::setUp(); | ||
|
||
$this->createSchemaForModels( | ||
GH7877ApplicationGeneratedIdEntity::class, | ||
GH7877EntityWithNullableAssociation::class | ||
); | ||
} | ||
|
||
public function testSelfReferenceWithApplicationGeneratedIdMayBeNotNullable(): void | ||
{ | ||
$entity = new GH7877ApplicationGeneratedIdEntity(); | ||
$entity->parent = $entity; | ||
|
||
$this->expectNotToPerformAssertions(); | ||
|
||
$this->_em->persist($entity); | ||
$this->_em->flush(); | ||
} | ||
|
||
public function testCrossReferenceWithApplicationGeneratedIdMayBeNotNullable(): void | ||
{ | ||
$entity1 = new GH7877ApplicationGeneratedIdEntity(); | ||
$entity1->parent = $entity1; | ||
$entity2 = new GH7877ApplicationGeneratedIdEntity(); | ||
$entity2->parent = $entity1; | ||
|
||
$this->expectNotToPerformAssertions(); | ||
|
||
// As long as we do not have entity-level commit order computation | ||
// (see https://github.com/doctrine/orm/pull/10547), | ||
// this only works when the UoW processes $entity1 before $entity2, | ||
// so that the foreign key constraint E2 -> E1 can be satisfied. | ||
|
||
$this->_em->persist($entity1); | ||
$this->_em->persist($entity2); | ||
$this->_em->flush(); | ||
} | ||
|
||
public function testNullableForeignKeysMakeInsertOrderLessRelevant(): void | ||
{ | ||
$entity1 = new GH7877EntityWithNullableAssociation(); | ||
$entity1->parent = $entity1; | ||
$entity2 = new GH7877EntityWithNullableAssociation(); | ||
$entity2->parent = $entity1; | ||
|
||
$this->expectNotToPerformAssertions(); | ||
|
||
// In contrast to the previous test, this case demonstrates that with NULLable | ||
// associations, even without entity-level commit order computation | ||
// (see https://github.com/doctrine/orm/pull/10547), we can get away with an | ||
// insertion order of E2 before E1. That is because the UoW will schedule an extra | ||
// update that saves the day - the foreign key reference will established only after | ||
// all insertions have been performed. | ||
|
||
$this->_em->persist($entity2); | ||
$this->_em->persist($entity1); | ||
$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; | ||
|
||
public function __construct() | ||
{ | ||
$this->id = uniqid(); | ||
} | ||
} | ||
|
||
/** | ||
* @ORM\Entity | ||
*/ | ||
class GH7877EntityWithNullableAssociation | ||
{ | ||
/** | ||
* @ORM\Id | ||
* @ORM\Column(type="string") | ||
* @ORM\GeneratedValue(strategy="NONE") | ||
* | ||
* @var string | ||
*/ | ||
public $id; | ||
|
||
/** | ||
* @ORM\ManyToOne(targetEntity="GH7877EntityWithNullableAssociation") | ||
* @ORM\JoinColumn(name="parent_id", referencedColumnName="id", nullable=true) | ||
* | ||
* @var self | ||
*/ | ||
public $parent; | ||
|
||
public function __construct() | ||
{ | ||
$this->id = uniqid(); | ||
} | ||
} |