Skip to content

Commit f750132

Browse files
committed
Fix duplicate foreign key deprecation with Single Table Inheritance
When multiple STI child entities have a ManyToOne relation to the same target entity using the same join column, SchemaTool called addForeignKeyConstraint twice with the same constraint name, triggering a DBAL deprecation ("Overwriting an existing foreign key constraint"). The fix skips the redundant addForeignKeyConstraint call when an identical FK (same local columns, same foreign table, same foreign columns) was already added. See doctrine/dbal#7125
1 parent 4262eb4 commit f750132

2 files changed

Lines changed: 77 additions & 1 deletion

File tree

src/Tools/SchemaTool.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -834,7 +834,7 @@ class_exists(ForeignKeyConstraintEditor::class)
834834
}
835835

836836
$blacklistedFks[$compositeName] = true;
837-
} elseif (! isset($blacklistedFks[$compositeName])) {
837+
} elseif (! isset($blacklistedFks[$compositeName]) && ! isset($addedFks[$compositeName])) {
838838
$addedFks[$compositeName] = ['foreignTableName' => $foreignTableName, 'foreignColumns' => $foreignColumns];
839839
$theJoinTable->addForeignKeyConstraint(
840840
$foreignTableName,

tests/Tests/ORM/Tools/SchemaToolTest.php

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,13 @@
1717
use Doctrine\DBAL\Types\Types;
1818
use Doctrine\ORM\Mapping\ClassMetadata;
1919
use Doctrine\ORM\Mapping\Column;
20+
use Doctrine\ORM\Mapping\DiscriminatorColumn;
21+
use Doctrine\ORM\Mapping\DiscriminatorMap;
2022
use Doctrine\ORM\Mapping\Entity;
23+
use Doctrine\ORM\Mapping\GeneratedValue;
2124
use Doctrine\ORM\Mapping\Id;
2225
use Doctrine\ORM\Mapping\Index;
26+
use Doctrine\ORM\Mapping\InheritanceType;
2327
use Doctrine\ORM\Mapping\JoinColumn;
2428
use Doctrine\ORM\Mapping\ManyToOne;
2529
use Doctrine\ORM\Mapping\MappingException;
@@ -523,6 +527,39 @@ private static function columnIsIndexed(DbalTable $table, string $column): bool
523527

524528
return false;
525529
}
530+
531+
public function testSingleTableInheritanceWithSameForeignKeyInChildrenDoesNotTriggerDeprecation(): void
532+
{
533+
$em = $this->getTestEntityManager();
534+
$schemaTool = new SchemaTool($em);
535+
536+
$schema = $schemaTool->getSchemaFromMetadata([
537+
$em->getClassMetadata(STIBox::class),
538+
$em->getClassMetadata(STILocation::class),
539+
$em->getClassMetadata(STIBoxContainer::class),
540+
$em->getClassMetadata(STIPackedBoxContainer::class),
541+
]);
542+
543+
self::assertTrue($schema->hasTable('sti_location'));
544+
545+
$table = $schema->getTable('sti_location');
546+
547+
self::assertCount(1, $table->getForeignKeys());
548+
549+
$foreignKeys = $table->getForeignKeys();
550+
$foreignKey = current($foreignKeys);
551+
552+
if (class_exists(ForeignKeyConstraintEditor::class)) {
553+
self::assertSame('sti_box', $foreignKey->getReferencedTableName()->toString());
554+
self::assertSame(['box_id'], array_map(
555+
static fn (UnqualifiedName $name) => $name->toString(),
556+
$foreignKey->getReferencingColumnNames(),
557+
));
558+
} else {
559+
self::assertSame('sti_box', $foreignKey->getForeignTableName());
560+
self::assertSame(['box_id'], $foreignKey->getLocalColumns());
561+
}
562+
}
526563
}
527564

528565
#[Table(options: ['foo' => 'bar', 'baz' => ['key' => 'val']])]
@@ -758,3 +795,42 @@ class QuotedEntity
758795
#[Column(name: '`quoted-name`')]
759796
public string $name = '';
760797
}
798+
799+
#[Entity]
800+
#[Table(name: 'sti_box')]
801+
class STIBox
802+
{
803+
#[Id]
804+
#[Column(type: 'integer')]
805+
#[GeneratedValue]
806+
public int|null $id = null;
807+
}
808+
809+
#[Entity]
810+
#[Table(name: 'sti_location')]
811+
#[InheritanceType('SINGLE_TABLE')]
812+
#[DiscriminatorColumn(name: 'type', type: 'string')]
813+
#[DiscriminatorMap(['box_container' => STIBoxContainer::class, 'packed_box_container' => STIPackedBoxContainer::class])]
814+
abstract class STILocation
815+
{
816+
#[Id]
817+
#[Column(type: 'integer')]
818+
#[GeneratedValue]
819+
public int|null $id = null;
820+
}
821+
822+
#[Entity]
823+
class STIBoxContainer extends STILocation
824+
{
825+
#[ManyToOne(targetEntity: STIBox::class)]
826+
#[JoinColumn(name: 'box_id', referencedColumnName: 'id')]
827+
public STIBox|null $box = null;
828+
}
829+
830+
#[Entity]
831+
class STIPackedBoxContainer extends STILocation
832+
{
833+
#[ManyToOne(targetEntity: STIBox::class)]
834+
#[JoinColumn(name: 'box_id', referencedColumnName: 'id')]
835+
public STIBox|null $box = null;
836+
}

0 commit comments

Comments
 (0)