From 30b665813795b511d74014ec1a11b5df25864c98 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Sat, 12 Oct 2024 14:56:07 +0200 Subject: [PATCH] Auto-detect values for EnumType columns --- psalm-baseline.xml | 9 ++-- src/Mapping/ClassMetadata.php | 11 +++-- src/Mapping/DefaultTypedFieldMapper.php | 45 +++++++++++-------- tests/Tests/Models/Enums/CardNativeEnum.php | 25 +++++++++++ .../Models/Enums/TypedCardNativeEnum.php | 23 ++++++++++ tests/Tests/ORM/Functional/EnumTest.php | 21 ++++++--- 6 files changed, 101 insertions(+), 33 deletions(-) create mode 100644 tests/Tests/Models/Enums/CardNativeEnum.php create mode 100644 tests/Tests/Models/Enums/TypedCardNativeEnum.php diff --git a/psalm-baseline.xml b/psalm-baseline.xml index b83ae43a889..a284dd38e2d 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -212,14 +212,14 @@ - - - - + + + + @@ -402,6 +402,7 @@ + diff --git a/src/Mapping/ClassMetadata.php b/src/Mapping/ClassMetadata.php index 70d3ea7042f..dea4006df3a 100644 --- a/src/Mapping/ClassMetadata.php +++ b/src/Mapping/ClassMetadata.php @@ -7,6 +7,8 @@ use BackedEnum; use BadMethodCallException; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Types\EnumType; +use Doctrine\DBAL\Types\Types; use Doctrine\Deprecations\Deprecation; use Doctrine\Instantiator\Instantiator; use Doctrine\Instantiator\InstantiatorInterface; @@ -23,6 +25,7 @@ use ReflectionProperty; use Stringable; +use function array_column; use function array_diff; use function array_intersect; use function array_key_exists; @@ -1119,9 +1122,7 @@ private function validateAndCompleteTypedFieldMapping(array $mapping): array { $field = $this->reflClass->getProperty($mapping['fieldName']); - $mapping = $this->typedFieldMapper->validateAndComplete($mapping, $field); - - return $mapping; + return $this->typedFieldMapper->validateAndComplete($mapping, $field); } /** @@ -1232,6 +1233,10 @@ protected function validateAndCompleteFieldMapping(array $mapping): FieldMapping if (! empty($mapping->id)) { $this->containsEnumIdentifier = true; } + + if (class_exists(EnumType::class) && $mapping->type === Types::ENUM && ! isset($mapping->options['values'])) { + $mapping->options['values'] = array_column($mapping->enumType::cases(), 'value'); + } } return $mapping; diff --git a/src/Mapping/DefaultTypedFieldMapper.php b/src/Mapping/DefaultTypedFieldMapper.php index 49144b8b7c8..4a6eb86d4e5 100644 --- a/src/Mapping/DefaultTypedFieldMapper.php +++ b/src/Mapping/DefaultTypedFieldMapper.php @@ -49,30 +49,37 @@ public function validateAndComplete(array $mapping, ReflectionProperty $field): { $type = $field->getType(); + if (! $type instanceof ReflectionNamedType) { + return $mapping; + } + if ( - ! isset($mapping['type']) - && ($type instanceof ReflectionNamedType) + ! $type->isBuiltin() + && enum_exists($type->getName()) + && (! isset($mapping['type']) || $mapping['type'] === 'enum') ) { - if (! $type->isBuiltin() && enum_exists($type->getName())) { - $reflection = new ReflectionEnum($type->getName()); - if (! $reflection->isBacked()) { - throw MappingException::backedEnumTypeRequired( - $field->class, - $mapping['fieldName'], - $type->getName(), - ); - } + $reflection = new ReflectionEnum($type->getName()); + if (! $reflection->isBacked()) { + throw MappingException::backedEnumTypeRequired( + $field->class, + $mapping['fieldName'], + $type->getName(), + ); + } - assert(is_a($type->getName(), BackedEnum::class, true)); - $mapping['enumType'] = $type->getName(); - $type = $reflection->getBackingType(); + assert(is_a($type->getName(), BackedEnum::class, true)); + $mapping['enumType'] = $type->getName(); + $type = $reflection->getBackingType(); - assert($type instanceof ReflectionNamedType); - } + assert($type instanceof ReflectionNamedType); + } - if (isset($this->typedFieldMappings[$type->getName()])) { - $mapping['type'] = $this->typedFieldMappings[$type->getName()]; - } + if (isset($mapping['type'])) { + return $mapping; + } + + if (isset($this->typedFieldMappings[$type->getName()])) { + $mapping['type'] = $this->typedFieldMappings[$type->getName()]; } return $mapping; diff --git a/tests/Tests/Models/Enums/CardNativeEnum.php b/tests/Tests/Models/Enums/CardNativeEnum.php new file mode 100644 index 00000000000..8d06dba8e58 --- /dev/null +++ b/tests/Tests/Models/Enums/CardNativeEnum.php @@ -0,0 +1,25 @@ + ['H', 'D', 'C', 'S']])] + public $suit; +} diff --git a/tests/Tests/Models/Enums/TypedCardNativeEnum.php b/tests/Tests/Models/Enums/TypedCardNativeEnum.php new file mode 100644 index 00000000000..59e4eb00e55 --- /dev/null +++ b/tests/Tests/Models/Enums/TypedCardNativeEnum.php @@ -0,0 +1,23 @@ +_em->flush(); $this->_em->clear(); - $fetchedCard = $this->_em->find(Card::class, $card->id); + $fetchedCard = $this->_em->find($cardClass, $card->id); $this->assertInstanceOf(Suit::class, $fetchedCard->suit); $this->assertEquals(Suit::Clubs, $fetchedCard->suit); @@ -447,13 +451,16 @@ public function testEnumWithNonMatchingDatabaseValueThrowsException(string $card $this->_em->find($cardClass, $card->id); } - /** @return array */ - public static function provideCardClasses(): array + /** @return iterable */ + public static function provideCardClasses(): iterable { - return [ - Card::class => [Card::class], - TypedCard::class => [TypedCard::class], - ]; + yield Card::class => [Card::class]; + yield TypedCard::class => [TypedCard::class]; + + if (class_exists(EnumType::class)) { + yield CardNativeEnum::class => [CardNativeEnum::class]; + yield TypedCardNativeEnum::class => [TypedCardNativeEnum::class]; + } } public function testItAllowsReadingAttributes(): void