Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow setting foreign key as deferrable #10624

Open
wants to merge 1 commit into
base: 3.4.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/en/reference/attributes-reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,7 @@ Optional parameters:
- **unique**: Determines whether this relation is exclusive between the
affected entities and should be enforced as such on the database
constraint level. Defaults to false.
- **deferrable**: Determines whether this relation constraint can be deferred. Defaults to false.
- **nullable**: Determine whether the related entity is required, or if
null is an allowed state for the relation. Defaults to true.
- **onDelete**: Cascade Action (Database-level)
Expand Down
1 change: 1 addition & 0 deletions src/Mapping/Driver/AttributeDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,7 @@ private function joinColumnToArray(Mapping\JoinColumn|Mapping\InverseJoinColumn
{
$mapping = [
'name' => $joinColumn->name,
'deferrable' => $joinColumn->deferrable,
'unique' => $joinColumn->unique,
'nullable' => $joinColumn->nullable,
'onDelete' => $joinColumn->onDelete,
Expand Down
3 changes: 2 additions & 1 deletion src/Mapping/JoinColumnMapping.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ final class JoinColumnMapping implements ArrayAccess
{
use ArrayAccessImplementation;

public bool|null $deferrable = null;
public bool|null $unique = null;
public bool|null $quoted = null;
public string|null $fieldName = null;
Expand Down Expand Up @@ -66,7 +67,7 @@ public function __sleep(): array
}
}

foreach (['unique', 'quoted', 'nullable'] as $boolKey) {
foreach (['deferrable', 'unique', 'quoted', 'nullable'] as $boolKey) {
if ($this->$boolKey !== null) {
$serialized[] = $boolKey;
}
Expand Down
1 change: 1 addition & 0 deletions src/Mapping/JoinColumnProperties.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ trait JoinColumnProperties
public function __construct(
public readonly string|null $name = null,
public readonly string|null $referencedColumnName = null,
public readonly bool $deferrable = false,
public readonly bool $unique = false,
public readonly bool $nullable = true,
public readonly mixed $onDelete = null,
Expand Down
4 changes: 4 additions & 0 deletions src/Tools/SchemaTool.php
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,10 @@ private function gatherRelationJoinColumns(
if (isset($joinColumn->onDelete)) {
$fkOptions['onDelete'] = $joinColumn->onDelete;
}

if (isset($joinColumn->deferrable)) {
$fkOptions['deferrable'] = $joinColumn->deferrable;
}
}

// Prefer unique constraints over implicit simple indexes created for foreign keys.
Expand Down
32 changes: 32 additions & 0 deletions tests/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use PHPUnit\Framework\Attributes\Group;

use function array_filter;
use function array_values;
use function implode;
use function str_starts_with;

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

self::assertCount(0, $sql, implode("\n", $sql));
}

public function testSetDeferrableForeignKey(): void
{
$schema = $this->getSchemaForModels(
EntityWithSelfReferencingAssociation::class,
);

$table = $schema->getTable('entitywithselfreferencingassociation');
$fks = array_values($table->getForeignKeys());

self::assertCount(1, $fks);

self::assertTrue($fks[0]->getOption('deferrable'));
}
}

#[Table(name: 'stonewood.screen')]
Expand Down Expand Up @@ -98,3 +113,20 @@ class DDC1657Avatar
#[Column(name: 'pk', type: 'integer', nullable: false)]
private int $pk;
}

#[Table(name: 'entitywithselfreferencingassociation')]
#[Entity]
class EntityWithSelfReferencingAssociation
{
/**
* Identifier
*/
#[Id]
#[GeneratedValue(strategy: 'IDENTITY')]
#[Column(type: 'integer', nullable: false)]
private int $id;

#[ManyToOne(targetEntity: self::class)]
#[JoinColumn(deferrable: true)]
private self $parent;
}
2 changes: 2 additions & 0 deletions tests/Tests/ORM/Mapping/JoinColumnMappingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public function testItSurvivesSerialization(): void
{
$mapping = new JoinColumnMapping('foo', 'id');

$mapping->deferrable = true;
$mapping->unique = true;
$mapping->quoted = true;
$mapping->fieldName = 'bar';
Expand All @@ -29,6 +30,7 @@ public function testItSurvivesSerialization(): void
$resurrectedMapping = unserialize(serialize($mapping));
assert($resurrectedMapping instanceof JoinColumnMapping);

self::assertTrue($resurrectedMapping->deferrable);
self::assertSame('foo', $resurrectedMapping->name);
self::assertTrue($resurrectedMapping->unique);
self::assertTrue($resurrectedMapping->quoted);
Expand Down