Skip to content

Commit 64444dc

Browse files
authored
Merge pull request #12088 from greg0ire/quote-parts
Quote parts of the table name
2 parents eb2cd53 + 85d66de commit 64444dc

File tree

5 files changed

+122
-40
lines changed

5 files changed

+122
-40
lines changed

src/Mapping/ClassMetadata.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -400,13 +400,9 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
400400
public DiscriminatorColumnMapping|null $discriminatorColumn = null;
401401

402402
/**
403-
* READ-ONLY: The primary table definition. The definition is an array with the
404-
* following entries:
403+
* READ-ONLY: The primary table definition.
405404
*
406-
* name => <tableName>
407-
* schema => <schemaName>
408-
* indexes => array
409-
* uniqueConstraints => array
405+
* "quoted" indicates whether the table name is quoted (with backticks) or not
410406
*
411407
* @var mixed[]
412408
* @phpstan-var array{

src/Mapping/DefaultQuoteStrategy.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@
1010
use function array_map;
1111
use function array_merge;
1212
use function assert;
13+
use function explode;
14+
use function implode;
1315
use function is_numeric;
1416
use function preg_replace;
17+
use function sprintf;
1518
use function substr;
1619

1720
/**
@@ -38,7 +41,13 @@ public function getTableName(ClassMetadata $class, AbstractPlatform $platform):
3841
$tableName = $class->table['name'];
3942

4043
if (! empty($class->table['schema'])) {
41-
$tableName = $class->table['schema'] . '.' . $class->table['name'];
44+
return isset($class->table['quoted'])
45+
? sprintf(
46+
'%s.%s',
47+
$platform->quoteSingleIdentifier($class->table['schema']),
48+
$platform->quoteSingleIdentifier($tableName),
49+
)
50+
: $class->table['schema'] . '.' . $class->table['name'];
4251
}
4352

4453
return isset($class->table['quoted'])
@@ -52,7 +61,10 @@ public function getTableName(ClassMetadata $class, AbstractPlatform $platform):
5261
public function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform): string
5362
{
5463
return isset($definition['quoted'])
55-
? $platform->quoteSingleIdentifier($definition['sequenceName'])
64+
? implode('.', array_map(
65+
static fn (string $part) => $platform->quoteSingleIdentifier($part),
66+
explode('.', $definition['sequenceName']),
67+
))
5668
: $definition['sequenceName'];
5769
}
5870

tests/Tests/ORM/Functional/Ticket/DDC2825Test.php

Lines changed: 16 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
use PHPUnit\Framework\Attributes\DataProvider;
1212
use PHPUnit\Framework\Attributes\Group;
1313

14-
use function sprintf;
15-
1614
/**
1715
* This class makes tests on the correct use of a database schema when entities are stored
1816
*/
@@ -30,28 +28,7 @@ protected function setUp(): void
3028
}
3129
}
3230

33-
#[DataProvider('getTestedClasses')]
34-
public function testClassSchemaMappingsValidity(string $className, string $expectedSchemaName, string $expectedTableName): void
35-
{
36-
$classMetadata = $this->_em->getClassMetadata($className);
37-
$platform = $this->_em->getConnection()->getDatabasePlatform();
38-
$quotedTableName = $this->_em->getConfiguration()->getQuoteStrategy()->getTableName($classMetadata, $platform);
39-
40-
// Check if table name and schema properties are defined in the class metadata
41-
self::assertEquals($expectedTableName, $classMetadata->table['name']);
42-
self::assertEquals($expectedSchemaName, $classMetadata->table['schema']);
43-
44-
$fullTableName = sprintf('%s.%s', $expectedSchemaName, $expectedTableName);
45-
46-
self::assertEquals($fullTableName, $quotedTableName);
47-
48-
// Checks sequence name validity
49-
self::assertEquals(
50-
$fullTableName . '_' . $classMetadata->getSingleIdentifierColumnName() . '_seq',
51-
$classMetadata->getSequenceName($platform),
52-
);
53-
}
54-
31+
/** @param class-string $className */
5532
#[DataProvider('getTestedClasses')]
5633
public function testPersistenceOfEntityWithSchemaMapping(string $className): void
5734
{
@@ -64,17 +41,14 @@ public function testPersistenceOfEntityWithSchemaMapping(string $className): voi
6441
self::assertCount(1, $this->_em->getRepository($className)->findAll());
6542
}
6643

67-
/**
68-
* Data provider
69-
*
70-
* @return string[][]
71-
*/
44+
/** @return list<array{class-string}> */
7245
public static function getTestedClasses(): array
7346
{
7447
return [
75-
[ExplicitSchemaAndTable::class, 'explicit_schema', 'explicit_table'],
76-
[SchemaAndTableInTableName::class, 'implicit_schema', 'implicit_table'],
77-
[DDC2825ClassWithImplicitlyDefinedSchemaAndQuotedTableName::class, 'myschema', 'order'],
48+
[ExplicitSchemaAndTable::class],
49+
[SchemaAndTableInTableName::class],
50+
[DDC2825ClassWithImplicitlyDefinedSchemaAndQuotedTableName::class],
51+
[File::class],
7852
];
7953
}
8054
}
@@ -89,3 +63,13 @@ class DDC2825ClassWithImplicitlyDefinedSchemaAndQuotedTableName
8963
#[ORM\Column(type: 'integer')]
9064
public $id;
9165
}
66+
67+
#[ORM\Entity]
68+
#[ORM\Table(name: '`file`', schema: 'yourschema')]
69+
class File
70+
{
71+
#[ORM\Id]
72+
#[ORM\Column]
73+
#[ORM\GeneratedValue]
74+
public int $id;
75+
}

tests/Tests/ORM/Mapping/ClassMetadataTest.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Doctrine\Tests\ORM\Mapping;
66

77
use ArrayObject;
8+
use Doctrine\DBAL\Platforms\AbstractPlatform;
89
use Doctrine\DBAL\Types\Types;
910
use Doctrine\Deprecations\PHPUnit\VerifyDeprecations;
1011
use Doctrine\ORM\Events;
@@ -51,6 +52,7 @@
5152
use Doctrine\Tests\OrmTestCase;
5253
use DoctrineGlobalArticle;
5354
use LogicException;
55+
use PHPUnit\Framework\Attributes\DataProvider;
5456
use PHPUnit\Framework\Attributes\Group as TestGroup;
5557
use PHPUnit\Framework\Attributes\WithoutErrorHandler;
5658
use ReflectionClass;
@@ -970,6 +972,47 @@ public function testQuotedSequenceName(): void
970972
);
971973
}
972974

975+
#[DataProvider('fullTableNameProvider')]
976+
public function testGetSequenceName(
977+
string $expectedSequenceName,
978+
string $fullTableName,
979+
): void {
980+
$cm = new ClassMetadata(self::class);
981+
$cm->setIdentifier(['id']);
982+
$cm->setPrimaryTable(['name' => $fullTableName]);
983+
984+
$platform = $this->createStub(AbstractPlatform::class);
985+
986+
self::assertSame(
987+
$expectedSequenceName,
988+
$cm->getSequenceName($platform),
989+
);
990+
}
991+
992+
/** @return iterable<string, array{string, string}> */
993+
public static function fullTableNameProvider(): iterable
994+
{
995+
yield 'quoted table name with schema' => [
996+
'custom.reserved_id_seq',
997+
'custom.`reserved`',
998+
];
999+
1000+
yield 'unquoted table name with schema' => [
1001+
'custom.non_reserved_id_seq',
1002+
'custom.non_reserved',
1003+
];
1004+
1005+
yield 'quoted table name without schema' => [
1006+
'reserved_id_seq',
1007+
'`reserved`',
1008+
];
1009+
1010+
yield 'unquoted table name without schema' => [
1011+
'non_reserved_id_seq',
1012+
'non_reserved',
1013+
];
1014+
}
1015+
9731016
#[TestGroup('DDC-2700')]
9741017
public function testIsIdentifierMappedSuperClass(): void
9751018
{

tests/Tests/ORM/Mapping/DefaultQuoteStrategyTest.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@
66

77
use Doctrine\DBAL\Platforms\AbstractPlatform;
88
use Doctrine\DBAL\Schema\Name\UnquotedIdentifierFolding;
9+
use Doctrine\ORM\Mapping\ClassMetadata;
910
use Doctrine\ORM\Mapping\DefaultQuoteStrategy;
1011
use Doctrine\Tests\Models\NonPublicSchemaJoins\User as NonPublicSchemaUser;
1112
use Doctrine\Tests\OrmTestCase;
13+
use PHPUnit\Framework\Attributes\DataProvider;
1214
use PHPUnit\Framework\Attributes\Group;
1315

1416
use function assert;
1517
use function enum_exists;
18+
use function sprintf;
1619

1720
/**
1821
* Doctrine\Tests\ORM\Mapping\DefaultQuoteStrategyTest
@@ -34,4 +37,48 @@ public function testGetJoinTableName(): void
3437
$strategy->getJoinTableName($metadata->associationMappings['readers'], $metadata, $platform),
3538
);
3639
}
40+
41+
#[DataProvider('fullTableNameProvider')]
42+
public function testGetTableName(string $expectedFullTableName, string $tableName): void
43+
{
44+
$classMetadata = new ClassMetadata(self::class);
45+
$classMetadata->setPrimaryTable(['name' => $tableName]);
46+
47+
$platform = $this->createStub(AbstractPlatform::class);
48+
$platform->method('quoteSingleIdentifier')
49+
->willReturnCallback(
50+
static fn (string $identifier): string => sprintf('✌️%s✌️', $identifier),
51+
);
52+
53+
$quotedTableName = (new DefaultQuoteStrategy())->getTableName(
54+
$classMetadata,
55+
$platform,
56+
);
57+
58+
self::assertSame($expectedFullTableName, $quotedTableName);
59+
}
60+
61+
/** @return iterable<string, array{string, string}> */
62+
public static function fullTableNameProvider(): iterable
63+
{
64+
yield 'quoted table name with schema' => [
65+
'✌️custom✌️.✌️reserved✌️',
66+
'custom.`reserved`',
67+
];
68+
69+
yield 'unquoted table name with schema' => [
70+
'custom.non_reserved',
71+
'custom.non_reserved',
72+
];
73+
74+
yield 'quoted table name without schema' => [
75+
'✌️reserved✌️',
76+
'`reserved`',
77+
];
78+
79+
yield 'unquoted table name without schema' => [
80+
'non_reserved',
81+
'non_reserved',
82+
];
83+
}
3784
}

0 commit comments

Comments
 (0)