From b17da0734cdc88912d6d57415fb450ca0c7d3251 Mon Sep 17 00:00:00 2001 From: Michiel Vermeersch Date: Thu, 4 Sep 2025 13:28:17 +0200 Subject: [PATCH] Fixed schema comparison attempting to remove double foreign keys. --- src/Schema/Comparator.php | 15 +- .../Schema/DoubleForeignKeyConstraintTest.php | 183 ++++++++++++++++++ 2 files changed, 192 insertions(+), 6 deletions(-) create mode 100644 tests/Functional/Schema/DoubleForeignKeyConstraintTest.php diff --git a/src/Schema/Comparator.php b/src/Schema/Comparator.php index 6c06865dcf..145de2d59e 100644 --- a/src/Schema/Comparator.php +++ b/src/Schema/Comparator.php @@ -275,14 +275,17 @@ public function compareTables(Table $oldTable, Table $newTable): TableDiff foreach ($newForeignKeys as $newKey => $newForeignKey) { if ($this->diffForeignKey($oldForeignKey, $newForeignKey) === false) { unset($oldForeignKeys[$oldKey], $newForeignKeys[$newKey]); - } else { - if (strtolower($oldForeignKey->getName()) === strtolower($newForeignKey->getName())) { - $droppedForeignKeys[$oldKey] = $oldForeignKey; - $addedForeignKeys[$newKey] = $newForeignKey; + continue 2; + } - unset($oldForeignKeys[$oldKey], $newForeignKeys[$newKey]); - } + if (strtolower($oldForeignKey->getName()) !== strtolower($newForeignKey->getName())) { + continue; } + + $droppedForeignKeys[$oldKey] = $oldForeignKey; + $addedForeignKeys[$newKey] = $newForeignKey; + + unset($oldForeignKeys[$oldKey], $newForeignKeys[$newKey]); } } diff --git a/tests/Functional/Schema/DoubleForeignKeyConstraintTest.php b/tests/Functional/Schema/DoubleForeignKeyConstraintTest.php new file mode 100644 index 0000000000..48c918df7f --- /dev/null +++ b/tests/Functional/Schema/DoubleForeignKeyConstraintTest.php @@ -0,0 +1,183 @@ +schemaManager = $this->connection->createSchemaManager(); + } + + public function testDoubleForeignKeyConstraint(): void + { + $platform = $this->connection->getDatabasePlatform(); + if ($platform instanceof DB2Platform || $platform instanceof OraclePlatform) { + self::markTestSkipped('DB2 and Oracle do not allow multiple FKs with the same columns.'); + } + + $articles = Table::editor() + ->setUnquotedName('articles') + ->setColumns( + Column::editor() + ->setUnquotedName('id') + ->setTypeName(Types::INTEGER) + ->create(), + ) + ->setPrimaryKeyConstraint( + PrimaryKeyConstraint::editor() + ->setUnquotedColumnNames('id') + ->create(), + ) + ->create(); + + $orders = Table::editor() + ->setUnquotedName('orders') + ->setColumns( + Column::editor() + ->setUnquotedName('id') + ->setTypeName(Types::INTEGER) + ->create(), + Column::editor() + ->setUnquotedName('article_id') + ->setTypeName(Types::INTEGER) + ->create(), + ) + ->setForeignKeyConstraints( + ForeignKeyConstraint::editor() + ->setUnquotedName('articles_fk') + ->setUnquotedReferencingColumnNames('article_id') + ->setUnquotedReferencedTableName('articles') + ->setUnquotedReferencedColumnNames('id') + ->create(), + ForeignKeyConstraint::editor() + ->setUnquotedName('articles_fk_2') + ->setUnquotedReferencingColumnNames('article_id') + ->setUnquotedReferencedTableName('articles') + ->setUnquotedReferencedColumnNames('id') + ->create(), + ) + ->create(); + + $this->dropTableIfExists('orders'); + $this->dropTableIfExists('articles'); + + $this->connection->createSchemaManager() + ->createTable($articles); + $this->connection->createSchemaManager() + ->createTable($orders); + + $ordersActual = $this->schemaManager->introspectTable('orders'); + + self::assertTrue( + $this->schemaManager->createComparator() + ->compareTables($ordersActual, $orders) + ->isEmpty(), + ); + } + + public function testDoubleForeignKeyConstraintComparedToSingle(): void + { + $platform = $this->connection->getDatabasePlatform(); + if ($platform instanceof DB2Platform || $platform instanceof OraclePlatform) { + self::markTestSkipped('DB2 and Oracle do not allow multiple FKs with the same columns.'); + } + + $articles = Table::editor() + ->setUnquotedName('articles') + ->setColumns( + Column::editor() + ->setUnquotedName('id') + ->setTypeName(Types::INTEGER) + ->create(), + ) + ->setPrimaryKeyConstraint( + PrimaryKeyConstraint::editor() + ->setUnquotedColumnNames('id') + ->create(), + ) + ->create(); + + $orders = Table::editor() + ->setUnquotedName('orders') + ->setColumns( + Column::editor() + ->setUnquotedName('id') + ->setTypeName(Types::INTEGER) + ->create(), + Column::editor() + ->setUnquotedName('article_id') + ->setTypeName(Types::INTEGER) + ->create(), + ) + ->setForeignKeyConstraints( + ForeignKeyConstraint::editor() + ->setUnquotedName('articles_fk') + ->setUnquotedReferencingColumnNames('article_id') + ->setUnquotedReferencedTableName('articles') + ->setUnquotedReferencedColumnNames('id') + ->create(), + ForeignKeyConstraint::editor() + ->setUnquotedName('articles_fk_2') + ->setUnquotedReferencingColumnNames('article_id') + ->setUnquotedReferencedTableName('articles') + ->setUnquotedReferencedColumnNames('id') + ->create(), + ) + ->create(); + + $ordersCompare = Table::editor() + ->setUnquotedName('orders') + ->setColumns( + Column::editor() + ->setUnquotedName('id') + ->setTypeName(Types::INTEGER) + ->create(), + Column::editor() + ->setUnquotedName('article_id') + ->setTypeName(Types::INTEGER) + ->create(), + ) + ->setForeignKeyConstraints( + ForeignKeyConstraint::editor() + ->setUnquotedName('articles_fk') + ->setUnquotedReferencingColumnNames('article_id') + ->setUnquotedReferencedTableName('articles') + ->setUnquotedReferencedColumnNames('id') + ->create(), + ) + ->create(); + + $this->dropTableIfExists('orders'); + $this->dropTableIfExists('articles'); + + $this->connection->createSchemaManager() + ->createTable($articles); + $this->connection->createSchemaManager() + ->createTable($orders); + + $ordersActual = $this->schemaManager->introspectTable('orders'); + + $diff = $this->schemaManager->createComparator() + ->compareTables($ordersActual, $ordersCompare); + + self::assertFalse($diff->isEmpty()); + self::assertCount(0, $diff->getAddedForeignKeys()); + self::assertCount(1, $diff->getDroppedForeignKeys()); + self::assertSame('articles_fk_2', $diff->getDroppedForeignKeys()[0]->getName()); + } +}