Skip to content

Commit ef0ff51

Browse files
committed
Allow enum discriminator columns
This commit adds enumType option to DiscriminatorColumn as well as support for custom DBAL types which use PHP enums for PHP values. Previously, the enumType option was completely missing, but also even using custom types that used PHP enums would end up in exception because ObjectHydrator would try to convert enums to string using (string) explicit conversion.
1 parent 284e814 commit ef0ff51

18 files changed

+395
-7
lines changed

docs/en/reference/attributes-reference.rst

+1
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ Optional parameters:
366366

367367
- **type**: By default this is string.
368368
- **length**: By default this is 255.
369+
- **enumType**: By default this is `null`. Allows to map discriminatorColumn value to PHP enum
369370

370371
.. _attrref_discriminatormap:
371372

doctrine-mapping.xsd

+1
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,7 @@
337337
<xs:attribute name="field-name" type="xs:NMTOKEN" />
338338
<xs:attribute name="length" type="xs:NMTOKEN" />
339339
<xs:attribute name="column-definition" type="xs:string" />
340+
<xs:attribute name="enum-type" type="xs:string" />
340341
<xs:anyAttribute namespace="##other"/>
341342
</xs:complexType>
342343

lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php

+1
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,7 @@ protected function hydrateColumnInfo($key)
617617
'fieldName' => $fieldName,
618618
'type' => $type,
619619
'dqlAlias' => $dqlAlias,
620+
'enumType' => $this->_rsm->enumMappings[$key] ?? null,
620621
];
621622
}
622623

lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php

+7-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Doctrine\ORM\Internal\Hydration;
66

7+
use BackedEnum;
78
use Doctrine\Common\Collections\ArrayCollection;
89
use Doctrine\ORM\Mapping\ClassMetadata;
910
use Doctrine\ORM\PersistentCollection;
@@ -246,7 +247,12 @@ private function getEntity(array $data, string $dqlAlias)
246247
}
247248

248249
$discrMap = $this->_metadataCache[$className]->discriminatorMap;
249-
$discriminatorValue = (string) $data[$discrColumn];
250+
$discriminatorValue = $data[$discrColumn];
251+
if ($discriminatorValue instanceof BackedEnum) {
252+
$discriminatorValue = $discriminatorValue->value;
253+
}
254+
255+
$discriminatorValue = (string) $discriminatorValue;
250256

251257
if (! isset($discrMap[$discriminatorValue])) {
252258
throw HydrationException::invalidDiscriminatorValue($discriminatorValue, array_keys($discrMap));

lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,7 @@ class ClassMetadataInfo implements ClassMetadata
574574
* READ-ONLY: The definition of the discriminator column used in JOINED and SINGLE_TABLE
575575
* inheritance mappings.
576576
*
577-
* @psalm-var array{name: string, fieldName: string, type: string, length?: int, columnDefinition?: string|null}|null
577+
* @psalm-var array{name: string, fieldName: string, type: string, length?: int, columnDefinition?: string|null, enumType?: class-string<BackedEnum>|null}|null
578578
*/
579579
public $discriminatorColumn;
580580

@@ -3220,7 +3220,7 @@ public function addEntityListener($eventName, $class, $method)
32203220
* @see getDiscriminatorColumn()
32213221
*
32223222
* @param mixed[]|null $columnDef
3223-
* @psalm-param array{name: string|null, fieldName?: string, type?: string, length?: int, columnDefinition?: string|null}|null $columnDef
3223+
* @psalm-param array{name: string|null, fieldName?: string, type?: string, length?: int, columnDefinition?: string|null, enumType?: class-string<BackedEnum>|null}|null $columnDef
32243224
*
32253225
* @return void
32263226
*

lib/Doctrine/ORM/Mapping/DiscriminatorColumn.php

+10-1
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,24 @@ final class DiscriminatorColumn implements MappingAttribute
3939
*/
4040
public $columnDefinition;
4141

42+
/**
43+
* @var class-string<\BackedEnum>|null
44+
* @readonly
45+
*/
46+
public $enumType = null;
47+
48+
/** @param class-string<\BackedEnum>|null $enumType */
4249
public function __construct(
4350
?string $name = null,
4451
?string $type = null,
4552
?int $length = null,
46-
?string $columnDefinition = null
53+
?string $columnDefinition = null,
54+
?string $enumType = null
4755
) {
4856
$this->name = $name;
4957
$this->type = $type;
5058
$this->length = $length;
5159
$this->columnDefinition = $columnDefinition;
60+
$this->enumType = $enumType;
5261
}
5362
}

lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php

+1
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad
319319
'type' => $discrColumnAnnot->type ?: 'string',
320320
'length' => $discrColumnAnnot->length ?? 255,
321321
'columnDefinition' => $discrColumnAnnot->columnDefinition,
322+
'enumType' => $discrColumnAnnot->enumType,
322323
]
323324
);
324325
} else {

lib/Doctrine/ORM/Mapping/Driver/AttributeDriver.php

+1
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad
271271
'type' => isset($discrColumnAttribute->type) ? (string) $discrColumnAttribute->type : 'string',
272272
'length' => isset($discrColumnAttribute->length) ? (int) $discrColumnAttribute->length : 255,
273273
'columnDefinition' => isset($discrColumnAttribute->columnDefinition) ? (string) $discrColumnAttribute->columnDefinition : null,
274+
'enumType' => isset($discrColumnAttribute->enumType) ? (string) $discrColumnAttribute->enumType : null,
274275
]
275276
);
276277
} else {

lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php

+1
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad
210210
'type' => isset($discrColumn['type']) ? (string) $discrColumn['type'] : 'string',
211211
'length' => isset($discrColumn['length']) ? (int) $discrColumn['length'] : 255,
212212
'columnDefinition' => isset($discrColumn['column-definition']) ? (string) $discrColumn['column-definition'] : null,
213+
'enumType' => isset($discrColumn['enum-type']) ? (string) $discrColumn['enum-type'] : null,
213214
]
214215
);
215216
} else {

lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php

+1
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad
201201
'type' => isset($discrColumn['type']) ? (string) $discrColumn['type'] : 'string',
202202
'length' => isset($discrColumn['length']) ? (int) $discrColumn['length'] : 255,
203203
'columnDefinition' => isset($discrColumn['columnDefinition']) ? (string) $discrColumn['columnDefinition'] : null,
204+
'enumType' => isset($discrColumn['enumType']) ? (string) $discrColumn['enumType'] : null,
204205
]
205206
);
206207
} else {

lib/Doctrine/ORM/Query/SqlWalker.php

+3
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,9 @@ public function walkSelectClause($selectClause)
775775

776776
$this->rsm->setDiscriminatorColumn($dqlAlias, $columnAlias);
777777
$this->rsm->addMetaResult($dqlAlias, $columnAlias, $discrColumn['fieldName'], false, $discrColumn['type']);
778+
if (! empty($discrColumn['enumType'])) {
779+
$this->rsm->addEnumResult($columnAlias, $discrColumn['enumType']);
780+
}
778781
}
779782

780783
// Add foreign key columns to SQL, if necessary

psalm-baseline.xml

+3-2
Original file line numberDiff line numberDiff line change
@@ -785,7 +785,7 @@
785785
</UndefinedInterfaceMethod>
786786
</file>
787787
<file src="lib/Doctrine/ORM/Mapping/Driver/AttributeDriver.php">
788-
<InvalidArgument occurrences="1"/>
788+
<InvalidArgument occurrences="2"/>
789789
<InvalidArrayAccess occurrences="4">
790790
<code>$value[0]</code>
791791
<code>$value[0]</code>
@@ -865,7 +865,7 @@
865865
<code>addNamedNativeQuery</code>
866866
<code>addNamedQuery</code>
867867
</DeprecatedMethod>
868-
<InvalidArgument occurrences="4">
868+
<InvalidArgument occurrences="5">
869869
<code>$this-&gt;cacheToArray($manyToManyElement-&gt;cache)</code>
870870
<code>$this-&gt;cacheToArray($manyToOneElement-&gt;cache)</code>
871871
<code>$this-&gt;cacheToArray($oneToManyElement-&gt;cache)</code>
@@ -931,6 +931,7 @@
931931
<code>addNamedNativeQuery</code>
932932
<code>addNamedQuery</code>
933933
</DeprecatedMethod>
934+
<InvalidArgument occurrences="1"/>
934935
<InvalidDocblock occurrences="1">
935936
<code>private function cacheToArray(array $cacheMapping): array</code>
936937
</InvalidDocblock>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\Tests\Models\GH10288;
6+
7+
enum GH10288People: string
8+
{
9+
case BOSS = 'boss';
10+
case EMPLOYEE = 'employee';
11+
}

0 commit comments

Comments
 (0)