Skip to content

Commit be8da83

Browse files
authored
Merge pull request #10624 from simPod/deferrable
feat: allow setting foreign key as deferrable
2 parents f5ab687 + bd260d1 commit be8da83

File tree

7 files changed

+43
-1
lines changed

7 files changed

+43
-1
lines changed

docs/en/reference/attributes-reference.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,7 @@ Optional parameters:
672672
- **unique**: Determines whether this relation is exclusive between the
673673
affected entities and should be enforced as such on the database
674674
constraint level. Defaults to false.
675+
- **deferrable**: Determines whether this relation constraint can be deferred. Defaults to false.
675676
- **nullable**: Determine whether the related entity is required, or if
676677
null is an allowed state for the relation. Defaults to true.
677678
- **onDelete**: Cascade Action (Database-level)

src/Mapping/Driver/AttributeDriver.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,7 @@ private function joinColumnToArray(Mapping\JoinColumn|Mapping\InverseJoinColumn
684684
{
685685
$mapping = [
686686
'name' => $joinColumn->name,
687+
'deferrable' => $joinColumn->deferrable,
687688
'unique' => $joinColumn->unique,
688689
'nullable' => $joinColumn->nullable,
689690
'onDelete' => $joinColumn->onDelete,

src/Mapping/JoinColumnMapping.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ final class JoinColumnMapping implements ArrayAccess
1313
{
1414
use ArrayAccessImplementation;
1515

16+
public bool|null $deferrable = null;
1617
public bool|null $unique = null;
1718
public bool|null $quoted = null;
1819
public string|null $fieldName = null;
@@ -66,7 +67,7 @@ public function __sleep(): array
6667
}
6768
}
6869

69-
foreach (['unique', 'quoted', 'nullable'] as $boolKey) {
70+
foreach (['deferrable', 'unique', 'quoted', 'nullable'] as $boolKey) {
7071
if ($this->$boolKey !== null) {
7172
$serialized[] = $boolKey;
7273
}

src/Mapping/JoinColumnProperties.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ trait JoinColumnProperties
1010
public function __construct(
1111
public readonly string|null $name = null,
1212
public readonly string|null $referencedColumnName = null,
13+
public readonly bool $deferrable = false,
1314
public readonly bool $unique = false,
1415
public readonly bool $nullable = true,
1516
public readonly mixed $onDelete = null,

src/Tools/SchemaTool.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,10 @@ private function gatherRelationJoinColumns(
709709
if (isset($joinColumn->onDelete)) {
710710
$fkOptions['onDelete'] = $joinColumn->onDelete;
711711
}
712+
713+
if (isset($joinColumn->deferrable)) {
714+
$fkOptions['deferrable'] = $joinColumn->deferrable;
715+
}
712716
}
713717

714718
// Prefer unique constraints over implicit simple indexes created for foreign keys.

tests/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use PHPUnit\Framework\Attributes\Group;
1717

1818
use function array_filter;
19+
use function array_values;
1920
use function implode;
2021
use function str_starts_with;
2122

@@ -42,6 +43,20 @@ public function testUpdateSchemaWithPostgreSQLSchema(): void
4243

4344
self::assertCount(0, $sql, implode("\n", $sql));
4445
}
46+
47+
public function testSetDeferrableForeignKey(): void
48+
{
49+
$schema = $this->getSchemaForModels(
50+
EntityWithSelfReferencingAssociation::class,
51+
);
52+
53+
$table = $schema->getTable('entitywithselfreferencingassociation');
54+
$fks = array_values($table->getForeignKeys());
55+
56+
self::assertCount(1, $fks);
57+
58+
self::assertTrue($fks[0]->getOption('deferrable'));
59+
}
4560
}
4661

4762
#[Table(name: 'stonewood.screen')]
@@ -98,3 +113,20 @@ class DDC1657Avatar
98113
#[Column(name: 'pk', type: 'integer', nullable: false)]
99114
private int $pk;
100115
}
116+
117+
#[Table(name: 'entitywithselfreferencingassociation')]
118+
#[Entity]
119+
class EntityWithSelfReferencingAssociation
120+
{
121+
/**
122+
* Identifier
123+
*/
124+
#[Id]
125+
#[GeneratedValue(strategy: 'IDENTITY')]
126+
#[Column(type: 'integer', nullable: false)]
127+
private int $id;
128+
129+
#[ManyToOne(targetEntity: self::class)]
130+
#[JoinColumn(deferrable: true)]
131+
private self $parent;
132+
}

tests/Tests/ORM/Mapping/JoinColumnMappingTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public function testItSurvivesSerialization(): void
1717
{
1818
$mapping = new JoinColumnMapping('foo', 'id');
1919

20+
$mapping->deferrable = true;
2021
$mapping->unique = true;
2122
$mapping->quoted = true;
2223
$mapping->fieldName = 'bar';
@@ -29,6 +30,7 @@ public function testItSurvivesSerialization(): void
2930
$resurrectedMapping = unserialize(serialize($mapping));
3031
assert($resurrectedMapping instanceof JoinColumnMapping);
3132

33+
self::assertTrue($resurrectedMapping->deferrable);
3234
self::assertSame('foo', $resurrectedMapping->name);
3335
self::assertTrue($resurrectedMapping->unique);
3436
self::assertTrue($resurrectedMapping->quoted);

0 commit comments

Comments
 (0)