diff --git a/composer.json b/composer.json index 4543c9ba735..6a4d59d318a 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "composer-runtime-api": "^2", "ext-ctype": "*", "doctrine/collections": "^2.2", - "doctrine/dbal": "^3.8.2 || ^4", + "doctrine/dbal": "4.3.x-dev", "doctrine/deprecations": "^0.5.3 || ^1", "doctrine/event-manager": "^1.2 || ^2", "doctrine/inflector": "^1.4 || ^2.0", diff --git a/src/DbalCompatibility/ForeignKey.php b/src/DbalCompatibility/ForeignKey.php new file mode 100644 index 00000000000..cf041514028 --- /dev/null +++ b/src/DbalCompatibility/ForeignKey.php @@ -0,0 +1,59 @@ + */ + public function getReferencingColumns(AbstractPlatform $platform): array + { + if (! method_exists($this->foreignKey, 'getReferencingColumns')) { + return $this->foreignKey->getLocalColumns(); + } + + $namesAsStrings = []; + + foreach ($this->foreignKey->getReferencingColumnNames() as $name) { + $namesAsStrings[] = $name->toSQL($platform); + } + + return $namesAsStrings; + } + + /** @return array */ + public function getReferencedColumns(AbstractPlatform $platform): array + { + if (! method_exists($this->foreignKey, 'getReferencedColumns')) { + return $this->foreignKey->getForeignColumns(); + } + + $namesAsStrings = []; + + foreach ($this->foreignKey->getReferencedColumnNames() as $name) { + $namesAsStrings[] = $name->toSQL($platform); + } + + return $namesAsStrings; + } + + public function getReferencedTableName(AbstractPlatform $platform): string + { + if (! method_exists($this->foreignKey, 'getReferencedTableName')) { + return $this->foreignKey->getForeignTableName(); + } + + return $this->foreignKey->getReferencedTableName()->toSQL($platform); + } +} diff --git a/src/DbalCompatibility/Table.php b/src/DbalCompatibility/Table.php new file mode 100644 index 00000000000..0e127fbdf57 --- /dev/null +++ b/src/DbalCompatibility/Table.php @@ -0,0 +1,27 @@ + */ + public function getForeignKeys(): array + { + return array_map( + static fn (ForeignKeyConstraint $foreignKey) => new ForeignKey($foreignKey), + $this->table->getForeignKeys(), + ); + } +} diff --git a/src/Mapping/DefaultQuoteStrategy.php b/src/Mapping/DefaultQuoteStrategy.php index 6260336c027..b42de4e862b 100644 --- a/src/Mapping/DefaultQuoteStrategy.php +++ b/src/Mapping/DefaultQuoteStrategy.php @@ -24,7 +24,7 @@ class DefaultQuoteStrategy implements QuoteStrategy public function getColumnName(string $fieldName, ClassMetadata $class, AbstractPlatform $platform): string { return isset($class->fieldMappings[$fieldName]->quoted) - ? $platform->quoteIdentifier($class->fieldMappings[$fieldName]->columnName) + ? $platform->quoteSingleIdentifier($class->fieldMappings[$fieldName]->columnName) : $class->fieldMappings[$fieldName]->columnName; } @@ -42,7 +42,7 @@ public function getTableName(ClassMetadata $class, AbstractPlatform $platform): } return isset($class->table['quoted']) - ? $platform->quoteIdentifier($tableName) + ? $platform->quoteSingleIdentifier($tableName) : $tableName; } @@ -52,14 +52,14 @@ public function getTableName(ClassMetadata $class, AbstractPlatform $platform): public function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform): string { return isset($definition['quoted']) - ? $platform->quoteIdentifier($definition['sequenceName']) + ? $platform->quoteSingleIdentifier($definition['sequenceName']) : $definition['sequenceName']; } public function getJoinColumnName(JoinColumnMapping $joinColumn, ClassMetadata $class, AbstractPlatform $platform): string { return isset($joinColumn->quoted) - ? $platform->quoteIdentifier($joinColumn->name) + ? $platform->quoteSingleIdentifier($joinColumn->name) : $joinColumn->name; } @@ -69,7 +69,7 @@ public function getReferencedJoinColumnName( AbstractPlatform $platform, ): string { return isset($joinColumn->quoted) - ? $platform->quoteIdentifier($joinColumn->referencedColumnName) + ? $platform->quoteSingleIdentifier($joinColumn->referencedColumnName) : $joinColumn->referencedColumnName; } @@ -87,7 +87,7 @@ public function getJoinTableName( $tableName = $association->joinTable->name; if (isset($association->joinTable->quoted)) { - $tableName = $platform->quoteIdentifier($tableName); + $tableName = $platform->quoteSingleIdentifier($tableName); } return $schema . $tableName; @@ -113,7 +113,7 @@ public function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform $joinColumns = $assoc->joinColumns; $assocQuotedColumnNames = array_map( static fn (JoinColumnMapping $joinColumn) => isset($joinColumn->quoted) - ? $platform->quoteIdentifier($joinColumn->name) + ? $platform->quoteSingleIdentifier($joinColumn->name) : $joinColumn->name, $joinColumns, ); diff --git a/src/Tools/SchemaTool.php b/src/Tools/SchemaTool.php index 5057d1866a8..b01b0889003 100644 --- a/src/Tools/SchemaTool.php +++ b/src/Tools/SchemaTool.php @@ -11,6 +11,7 @@ use Doctrine\DBAL\Schema\Index; use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Table; +use Doctrine\ORM\DbalCompatibility\Table as CompatibilityTable; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Mapping\AssociationMapping; use Doctrine\ORM\Mapping\ClassMetadata; @@ -37,6 +38,7 @@ use function implode; use function in_array; use function is_numeric; +use function method_exists; use function strtolower; /** @@ -723,13 +725,16 @@ private function gatherRelationJoinColumns( && ($foreignTableName !== $addedFks[$compositeName]['foreignTableName'] || 0 < count(array_diff($foreignColumns, $addedFks[$compositeName]['foreignColumns']))) ) { - foreach ($theJoinTable->getForeignKeys() as $fkName => $key) { + $theCompatibilityTable = new CompatibilityTable($theJoinTable); + foreach ($theCompatibilityTable->getForeignKeys() as $fkName => $key) { if ( - count(array_diff($key->getLocalColumns(), $localColumns)) === 0 - && (($key->getForeignTableName() !== $foreignTableName) - || 0 < count(array_diff($key->getForeignColumns(), $foreignColumns))) + count(array_diff($key->getReferencingColumns($this->platform), $localColumns)) === 0 + && (($key->getReferencedTableName($this->platform) !== $foreignTableName) + || 0 < count(array_diff($key->getReferencedColumns($this->platform), $foreignColumns))) ) { - $theJoinTable->removeForeignKey($fkName); + method_exists($theJoinTable, 'dropForeignKey') + ? $theJoinTable->dropForeignKey($fkName) + : $theJoinTable->removeForeignKey($fkName); break; } } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2138Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2138Test.php index 58d029b0100..41fcee14d15 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2138Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2138Test.php @@ -24,6 +24,7 @@ use function assert; use function reset; +use function sprintf; class DDC2138Test extends OrmFunctionalTestCase { @@ -42,8 +43,8 @@ public function testForeignKeyOnSTIWithMultipleMapping(): void $table = $schema->getTable('users_followed_objects'); assert($table instanceof DbalTable); - self::assertTrue($table->columnsAreIndexed(['object_id'])); - self::assertTrue($table->columnsAreIndexed(['user_id'])); + self::assertColumnIsIndexed($table, 'object_id'); + self::assertColumnIsIndexed($table, 'user_id'); $foreignKeys = $table->getForeignKeys(); self::assertCount(1, $foreignKeys, 'user_id column has to have FK, but not object_id'); @@ -55,6 +56,19 @@ public function testForeignKeyOnSTIWithMultipleMapping(): void self::assertContains('user_id', $localColumns); self::assertCount(1, $localColumns); } + + private static function assertColumnIsIndexed(DbalTable $table, string $columnName): void + { + $columnsIsIndexed = false; + foreach ($table->getIndexes() as $index) { + if ($index->spansColumns([$columnName])) { + $columnsIsIndexed = true; + break; + } + } + + self::assertTrue($columnsIsIndexed, sprintf('Column %s should be indexed.', $columnName)); + } } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC832Test.php b/tests/Tests/ORM/Functional/Ticket/DDC832Test.php index ca7f948518e..834c7a5cf5a 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC832Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC832Test.php @@ -42,14 +42,14 @@ public function tearDown(): void $platform = $this->_em->getConnection()->getDatabasePlatform(); $sm = $this->createSchemaManager(); - $sm->dropTable($platform->quoteIdentifier('TREE_INDEX')); - $sm->dropTable($platform->quoteIdentifier('INDEX')); - $sm->dropTable($platform->quoteIdentifier('LIKE')); + $sm->dropTable($platform->quoteSingleIdentifier('TREE_INDEX')); + $sm->dropTable($platform->quoteSingleIdentifier('INDEX')); + $sm->dropTable($platform->quoteSingleIdentifier('LIKE')); // DBAL 3 if ($platform instanceof PostgreSQLPlatform && method_exists($platform, 'getIdentitySequenceName')) { - $sm->dropSequence($platform->quoteIdentifier('INDEX_id_seq')); - $sm->dropSequence($platform->quoteIdentifier('LIKE_id_seq')); + $sm->dropSequence($platform->quoteSingleIdentifier('INDEX_id_seq')); + $sm->dropSequence($platform->quoteSingleIdentifier('LIKE_id_seq')); } } diff --git a/tests/Tests/ORM/Tools/SchemaToolTest.php b/tests/Tests/ORM/Tools/SchemaToolTest.php index 36037fcf569..ebd6d55f27f 100644 --- a/tests/Tests/ORM/Tools/SchemaToolTest.php +++ b/tests/Tests/ORM/Tools/SchemaToolTest.php @@ -5,6 +5,7 @@ namespace Doctrine\Tests\ORM\Tools; use Doctrine\Common\Collections\Collection; +use Doctrine\ORM\DbalCompatibility\ForeignKey; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; @@ -65,7 +66,16 @@ public function testAddUniqueIndexForUniqueFieldAttribute(): void $schema = $schemaTool->getSchemaFromMetadata($classes); self::assertTrue($schema->hasTable('cms_users'), 'Table cms_users should exist.'); - self::assertTrue($schema->getTable('cms_users')->columnsAreIndexed(['username']), 'username column should be indexed.'); + + $usernameIsIndexed = false; + foreach ($schema->getTable('cms_users')->getIndexes() as $index) { + if ($index->spansColumns(['username'])) { + $usernameIsIndexed = true; + break; + } + } + + self::assertTrue($usernameIsIndexed, 'username column should be indexed.'); } public function testAttributeOptionsArgument(): void @@ -285,17 +295,20 @@ public function testDerivedCompositeKey(): void self::assertCount(2, $childTableForeignKeys); $expectedColumns = [ - 'joined_derived_identity' => [['keyPart1_id'], ['id']], - 'joined_derived_root' => [['keyPart1_id', 'keyPart2'], ['keyPart1_id', 'keyPart2']], + '"JOINED_DERIVED_IDENTITY"' => [['keyPart1_id'], ['id']], + '"JOINED_DERIVED_ROOT"' => [['keyPart1_id', 'keyPart2'], ['keyPart1_id', 'keyPart2']], ]; + $platform = $em->getConnection()->getDatabasePlatform(); + foreach ($childTableForeignKeys as $foreignKey) { - self::assertArrayHasKey($foreignKey->getForeignTableName(), $expectedColumns); + $compatForeignKey = new ForeignKey($foreignKey); + self::assertArrayHasKey($compatForeignKey->getReferencedTableName($platform), $expectedColumns); - [$localColumns, $foreignColumns] = $expectedColumns[$foreignKey->getForeignTableName()]; + [$localColumns, $foreignColumns] = $expectedColumns[$compatForeignKey->getReferencedTableName($platform)]; - self::assertSame($localColumns, $foreignKey->getLocalColumns()); - self::assertSame($foreignColumns, $foreignKey->getForeignColumns()); + self::assertSame($localColumns, $compatForeignKey->getReferencingColumns($platform)); + self::assertSame($foreignColumns, $compatForeignKey->getReferencedColumns($platform)); } } diff --git a/tests/Tests/OrmFunctionalTestCase.php b/tests/Tests/OrmFunctionalTestCase.php index f2cf5f81f18..5456ceb14df 100644 --- a/tests/Tests/OrmFunctionalTestCase.php +++ b/tests/Tests/OrmFunctionalTestCase.php @@ -614,7 +614,7 @@ protected function tearDown(): void } if (isset($this->_usedModelSets['directorytree'])) { - $conn->executeStatement('DELETE FROM ' . $platform->quoteIdentifier('file')); + $conn->executeStatement('DELETE FROM ' . $platform->quoteSingleIdentifier('file')); // MySQL doesn't know deferred deletions therefore only executing the second query gives errors. $conn->executeStatement('DELETE FROM Directory WHERE parentDirectory_id IS NOT NULL'); $conn->executeStatement('DELETE FROM Directory'); @@ -707,17 +707,17 @@ protected function tearDown(): void $conn->executeStatement( sprintf( 'UPDATE %s SET %s = NULL', - $platform->quoteIdentifier('quote-address'), - $platform->quoteIdentifier('user-id'), + $platform->quoteSingleIdentifier('quote-address'), + $platform->quoteSingleIdentifier('user-id'), ), ); - $conn->executeStatement('DELETE FROM ' . $platform->quoteIdentifier('quote-users-groups')); - $conn->executeStatement('DELETE FROM ' . $platform->quoteIdentifier('quote-group')); - $conn->executeStatement('DELETE FROM ' . $platform->quoteIdentifier('quote-phone')); - $conn->executeStatement('DELETE FROM ' . $platform->quoteIdentifier('quote-user')); - $conn->executeStatement('DELETE FROM ' . $platform->quoteIdentifier('quote-address')); - $conn->executeStatement('DELETE FROM ' . $platform->quoteIdentifier('quote-city')); + $conn->executeStatement('DELETE FROM ' . $platform->quoteSingleIdentifier('quote-users-groups')); + $conn->executeStatement('DELETE FROM ' . $platform->quoteSingleIdentifier('quote-group')); + $conn->executeStatement('DELETE FROM ' . $platform->quoteSingleIdentifier('quote-phone')); + $conn->executeStatement('DELETE FROM ' . $platform->quoteSingleIdentifier('quote-user')); + $conn->executeStatement('DELETE FROM ' . $platform->quoteSingleIdentifier('quote-address')); + $conn->executeStatement('DELETE FROM ' . $platform->quoteSingleIdentifier('quote-city')); } if (isset($this->_usedModelSets['vct_onetoone'])) { @@ -1108,7 +1108,9 @@ protected function dropAndCreateTable(Table $table): void { $schemaManager = $this->createSchemaManager(); $platform = $this->_em->getConnection()->getDatabasePlatform(); - $tableName = $table->getQuotedName($platform); + $tableName = method_exists($table, 'getObjectName') ? + $table->getObjectName($platform)->toSQL($platform) + : $table->getQuotedName($platform); $this->dropTableIfExists($tableName); $schemaManager->createTable($table);