Skip to content

Commit 01774c0

Browse files
authored
Merge pull request #12065 from whataboutpereira/fix-enum-discriminator-column
Use enum values from enumType in DiscriminatorColumn and check DiscriminatorMap values against it
2 parents dd4e8fe + 6f83166 commit 01774c0

3 files changed

Lines changed: 79 additions & 0 deletions

File tree

src/Mapping/ClassMetadata.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2204,6 +2204,20 @@ public function setDiscriminatorColumn(DiscriminatorColumnMapping|array|null $co
22042204
throw MappingException::invalidDiscriminatorColumnType($this->name, $columnDef['type']);
22052205
}
22062206

2207+
if (isset($columnDef['enumType'])) {
2208+
if (! enum_exists($columnDef['enumType'])) {
2209+
throw MappingException::nonEnumTypeMapped($this->name, $columnDef['fieldName'], $columnDef['enumType']);
2210+
}
2211+
2212+
if (
2213+
defined('Doctrine\DBAL\Types\Types::ENUM')
2214+
&& $columnDef['type'] === Types::ENUM
2215+
&& ! isset($columnDef['options']['values'])
2216+
) {
2217+
$columnDef['options']['values'] = array_column($columnDef['enumType']::cases(), 'value');
2218+
}
2219+
}
2220+
22072221
$this->discriminatorColumn = DiscriminatorColumnMapping::fromMappingArray($columnDef);
22082222
}
22092223
}
@@ -2222,6 +2236,8 @@ final public function getDiscriminatorColumn(): DiscriminatorColumnMapping
22222236
* Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
22232237
*
22242238
* @param array<int|string, string> $map
2239+
*
2240+
* @throws MappingException
22252241
*/
22262242
public function setDiscriminatorMap(array $map): void
22272243
{
@@ -2241,6 +2257,16 @@ public function setDiscriminatorMap(array $map): void
22412257
);
22422258
}
22432259

2260+
$values = $this->discriminatorColumn->options['values'] ?? null;
2261+
2262+
if ($values !== null) {
2263+
$diff = array_diff(array_keys($map), $values);
2264+
2265+
if ($diff !== []) {
2266+
throw MappingException::invalidEntriesInDiscriminatorMap(array_values($diff), $this->name, $this->discriminatorColumn->enumType);
2267+
}
2268+
}
2269+
22442270
foreach ($map as $value => $className) {
22452271
$this->addDiscriminatorMapClass($value, $className);
22462272
}

src/Mapping/MappingException.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,24 @@ public static function fileMappingDriversRequireConfiguredDirectoryPath(string|n
329329
);
330330
}
331331

332+
/**
333+
* Returns an exception that indicates that discriminator entries used in a discriminator map
334+
* does not exist in the backed enum provided by enumType option.
335+
*
336+
* @param array<int,int|string> $entries The discriminator entries that could not be found.
337+
* @param string $owningClass The class that declares the discriminator map.
338+
* @param string $enumType The enum that entries were checked against.
339+
*/
340+
public static function invalidEntriesInDiscriminatorMap(array $entries, string $owningClass, string $enumType): self
341+
{
342+
return new self(sprintf(
343+
"The entries %s in the discriminator map of class '%s' do not correspond to enum cases of '%s'.",
344+
implode(', ', array_map(static fn ($entry): string => sprintf("'%s'", $entry), $entries)),
345+
$owningClass,
346+
$enumType,
347+
));
348+
}
349+
332350
/**
333351
* Returns an exception that indicates that a class used in a discriminator map does not exist.
334352
* An example would be an outdated (maybe renamed) classname.

tests/Tests/ORM/Tools/SchemaToolTest.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
use Doctrine\DBAL\Schema\PrimaryKeyConstraint;
1414
use Doctrine\DBAL\Schema\PrimaryKeyConstraintEditor;
1515
use Doctrine\DBAL\Schema\Table as DbalTable;
16+
use Doctrine\DBAL\Types\EnumType;
17+
use Doctrine\DBAL\Types\Types;
1618
use Doctrine\ORM\Mapping\ClassMetadata;
1719
use Doctrine\ORM\Mapping\Column;
1820
use Doctrine\ORM\Mapping\Entity;
@@ -46,6 +48,7 @@
4648
use Doctrine\Tests\Models\Enums\Suit;
4749
use Doctrine\Tests\Models\Forum\ForumAvatar;
4850
use Doctrine\Tests\Models\Forum\ForumUser;
51+
use Doctrine\Tests\Models\GH10288\GH10288People;
4952
use Doctrine\Tests\Models\NullDefault\NullDefaultColumn;
5053
use Doctrine\Tests\OrmTestCase;
5154
use PHPUnit\Framework\Attributes\Group;
@@ -272,6 +275,38 @@ public function testSetDiscriminatorColumnWithoutLength(): void
272275
self::assertEquals(255, $column->getLength());
273276
}
274277

278+
public function testSetDiscriminatorColumnWithEnumType(): void
279+
{
280+
if (! class_exists(EnumType::class)) {
281+
self::markTestSkipped('Test valid for doctrine/dbal versions with EnumType only.');
282+
}
283+
284+
$em = $this->getTestEntityManager();
285+
$schemaTool = new SchemaTool($em);
286+
$metadata = $em->getClassMetadata(FirstEntity::class);
287+
288+
$metadata->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE);
289+
$metadata->setDiscriminatorColumn([
290+
'name' => 'discriminator',
291+
'type' => Types::ENUM,
292+
'enumType' => GH10288People::class,
293+
]);
294+
295+
$schema = $schemaTool->getSchemaFromMetadata([$metadata]);
296+
297+
self::assertTrue($schema->hasTable('first_entity'));
298+
$table = $schema->getTable('first_entity');
299+
300+
self::assertTrue($table->hasColumn('discriminator'));
301+
$column = $table->getColumn('discriminator');
302+
self::assertEquals(GH10288People::class, $column->getPlatformOption('enumType'));
303+
self::assertEquals([0 => 'boss', 1 => 'employee'], $column->getValues());
304+
305+
$this->expectException(MappingException::class);
306+
$this->expectExceptionMessage("The entries 'user' in the discriminator map of class '" . FirstEntity::class . "' do not correspond to enum cases of '" . GH10288People::class . "'.");
307+
$metadata->setDiscriminatorMap(['user' => CmsUser::class, 'employee' => CmsEmployee::class]);
308+
}
309+
275310
public function testDerivedCompositeKey(): void
276311
{
277312
$em = $this->getTestEntityManager();

0 commit comments

Comments
 (0)