From fe46ab1ef09e9cacfa681da1877ac6e0273c34c2 Mon Sep 17 00:00:00 2001 From: Meron Nagy Date: Tue, 6 May 2025 17:27:08 +0200 Subject: [PATCH] feat(doctrine): support integer-backed enums in BackedEnumFilter --- .../Common/Filter/BackedEnumFilterTrait.php | 27 ++++++++---- src/Doctrine/Orm/Filter/BackedEnumFilter.php | 22 ++++++++-- .../Orm/Tests/Filter/BackedEnumFilterTest.php | 9 ++++ .../Filter/BackedEnumFilterTestTrait.php | 41 +++++++++++++++++++ 4 files changed, 87 insertions(+), 12 deletions(-) diff --git a/src/Doctrine/Common/Filter/BackedEnumFilterTrait.php b/src/Doctrine/Common/Filter/BackedEnumFilterTrait.php index fbc5b0fea6e..1ec8c05cf31 100644 --- a/src/Doctrine/Common/Filter/BackedEnumFilterTrait.php +++ b/src/Doctrine/Common/Filter/BackedEnumFilterTrait.php @@ -53,15 +53,26 @@ public function getDescription(string $resourceClass): array continue; } $propertyName = $this->normalizePropertyName($property); - $description[$propertyName] = [ - 'property' => $propertyName, - 'type' => 'string', - 'required' => false, - 'schema' => [ + $filterParameterNames = [$propertyName]; + $filterParameterNames[] = $propertyName.'[]'; + + foreach ($filterParameterNames as $filterParameterName) { + $isCollection = str_ends_with($filterParameterName, '[]'); + + $enumValues = array_map(fn (\BackedEnum $case) => $case->value, $this->enumTypes[$property]::cases()); + + $schema = $isCollection + ? ['type' => 'array', 'items' => ['type' => 'string', 'enum' => $enumValues]] + : ['type' => 'string', 'enum' => $enumValues]; + + $description[$filterParameterName] = [ + 'property' => $propertyName, 'type' => 'string', - 'enum' => array_map(fn (\BackedEnum $case) => $case->value, $this->enumTypes[$property]::cases()), - ], - ]; + 'required' => false, + 'is_collection' => $isCollection, + 'schema' => $schema, + ]; + } } return $description; diff --git a/src/Doctrine/Orm/Filter/BackedEnumFilter.php b/src/Doctrine/Orm/Filter/BackedEnumFilter.php index fffcb13a160..db8e86a31b4 100644 --- a/src/Doctrine/Orm/Filter/BackedEnumFilter.php +++ b/src/Doctrine/Orm/Filter/BackedEnumFilter.php @@ -125,8 +125,14 @@ protected function filterProperty(string $property, mixed $value, QueryBuilder $ return; } - $value = $this->normalizeValue($value, $property); - if (null === $value) { + $values = \is_array($value) ? $value : [$value]; + + $normalizedValues = array_filter(array_map( + fn ($v) => $this->normalizeValue($v, $property), + $values + )); + + if (empty($normalizedValues)) { return; } @@ -139,9 +145,17 @@ protected function filterProperty(string $property, mixed $value, QueryBuilder $ $valueParameter = $queryNameGenerator->generateParameterName($field); + if (1 === \count($values)) { + $queryBuilder + ->andWhere(\sprintf('%s.%s = :%s', $alias, $field, $valueParameter)) + ->setParameter($valueParameter, $values[0]); + + return; + } + $queryBuilder - ->andWhere(\sprintf('%s.%s = :%s', $alias, $field, $valueParameter)) - ->setParameter($valueParameter, $value); + ->andWhere(\sprintf('%s.%s IN (:%s)', $alias, $field, $valueParameter)) + ->setParameter($valueParameter, $values); } /** diff --git a/src/Doctrine/Orm/Tests/Filter/BackedEnumFilterTest.php b/src/Doctrine/Orm/Tests/Filter/BackedEnumFilterTest.php index 4522e0b0db6..5f782daa457 100644 --- a/src/Doctrine/Orm/Tests/Filter/BackedEnumFilterTest.php +++ b/src/Doctrine/Orm/Tests/Filter/BackedEnumFilterTest.php @@ -43,6 +43,15 @@ public static function provideApplyTestData(): array 'invalid case for nested property' => [ \sprintf('SELECT o FROM %s o', Dummy::class), ], + 'valid case (multiple values)' => [ + \sprintf('SELECT o FROM %s o WHERE o.dummyBackedEnum IN (:dummyBackedEnum_p1)', Dummy::class), + [ + 'dummyBackedEnum_p1' => [ + 'one', + 'two', + ], + ], + ], ] ); } diff --git a/src/Doctrine/Orm/Tests/Filter/BackedEnumFilterTestTrait.php b/src/Doctrine/Orm/Tests/Filter/BackedEnumFilterTestTrait.php index ebfb2a78010..bb864d16331 100644 --- a/src/Doctrine/Orm/Tests/Filter/BackedEnumFilterTestTrait.php +++ b/src/Doctrine/Orm/Tests/Filter/BackedEnumFilterTestTrait.php @@ -32,11 +32,25 @@ public function testGetDescription(): void 'property' => 'dummyBackedEnum', 'type' => 'string', 'required' => false, + 'is_collection' => false, 'schema' => [ 'type' => 'string', 'enum' => ['one', 'two'], ], ], + 'dummyBackedEnum[]' => [ + 'property' => 'dummyBackedEnum', + 'type' => 'string', + 'required' => false, + 'is_collection' => true, + 'schema' => [ + 'type' => 'array', + 'items' => [ + 'type' => 'string', + 'enum' => ['one', 'two'], + ], + ], + ], ], $filter->getDescription($this->resourceClass)); } @@ -49,11 +63,25 @@ public function testGetDescriptionDefaultFields(): void 'property' => 'dummyBackedEnum', 'type' => 'string', 'required' => false, + 'is_collection' => false, 'schema' => [ 'type' => 'string', 'enum' => ['one', 'two'], ], ], + 'dummyBackedEnum[]' => [ + 'property' => 'dummyBackedEnum', + 'type' => 'string', + 'required' => false, + 'is_collection' => true, + 'schema' => [ + 'type' => 'array', + 'items' => [ + 'type' => 'string', + 'enum' => ['one', 'two'], + ], + ], + ], ], $filter->getDescription($this->resourceClass)); } @@ -100,6 +128,19 @@ private static function provideApplyTestArguments(): array 'relatedDummy.dummyBackedEnum' => 'foo', ], ], + 'valid case (multiple values)' => [ + [ + 'id' => null, + 'name' => null, + 'dummyBackedEnum' => null, + ], + [ + 'dummyBackedEnum' => [ + 'one', + 'two', + ], + ], + ], ]; } }