From a5d65c4c03a66318c8faf95215cab37cfd55918a Mon Sep 17 00:00:00 2001 From: meyerbaptiste Date: Tue, 9 Feb 2021 17:03:45 +0100 Subject: [PATCH] WIP: feat: add support for "@SerializedName" annotation with filters --- features/doctrine/search_filter.feature | 20 +-- features/graphql/filters.feature | 4 +- .../Common/Filter/BooleanFilterTrait.php | 4 +- .../Common/Filter/DateFilterTrait.php | 27 +++- .../Common/Filter/ExistsFilterTrait.php | 4 +- .../Common/Filter/NumericFilterTrait.php | 4 +- .../Common/Filter/OrderFilterTrait.php | 4 +- .../Common/Filter/RangeFilterTrait.php | 29 ++-- .../Common/Filter/SearchFilterTrait.php | 4 +- .../Util/PropertyNameNormalizerTrait.php | 130 ++++++++++++++++++ .../MongoDbOdm/Filter/AbstractFilter.php | 27 ++-- .../MongoDbOdm/Filter/ExistsFilter.php | 2 +- .../MongoDbOdm/Filter/OrderFilter.php | 2 +- .../Orm/Filter/AbstractContextAwareFilter.php | 2 +- .../Doctrine/Orm/Filter/AbstractFilter.php | 25 +--- .../Doctrine/Orm/Filter/ExistsFilter.php | 2 +- .../Doctrine/Orm/Filter/OrderFilter.php | 2 +- tests/Fixtures/TestBundle/Entity/DummyCar.php | 1 + 18 files changed, 214 insertions(+), 79 deletions(-) create mode 100644 src/Bridge/Doctrine/Common/Util/PropertyNameNormalizerTrait.php diff --git a/features/doctrine/search_filter.feature b/features/doctrine/search_filter.feature index c396159bf77..ca4dc5ea61c 100644 --- a/features/doctrine/search_filter.feature +++ b/features/doctrine/search_filter.feature @@ -17,7 +17,7 @@ Feature: Search filter on collections @createSchema Scenario: Test #944 Given there is a DummyCar entity with related colors - When I send a "GET" request to "/dummy_cars?colors.prop=red" + When I send a "GET" request to "/dummy_cars?couleurs.prop=red" Then the response status code should be 200 And the JSON should be equal to: """ @@ -29,7 +29,7 @@ Feature: Search filter on collections { "@id": "/dummy_cars/1", "@type": "DummyCar", - "colors": [ + "couleurs": [ { "@id": "/dummy_car_colors/1", "@type": "DummyCarColor", @@ -71,12 +71,12 @@ Feature: Search filter on collections ], "hydra:totalItems": 1, "hydra:view": { - "@id": "/dummy_cars?colors.prop=red", + "@id": "/dummy_cars?couleurs.prop=red", "@type": "hydra:PartialCollectionView" }, "hydra:search": { "@type": "hydra:IriTemplate", - "hydra:template": "/dummy_cars{?availableAt[before],availableAt[strictly_before],availableAt[after],availableAt[strictly_after],canSell,foobar[],foobargroups[],foobargroups_override[],colors.prop,colors,colors[],secondColors,secondColors[],thirdColors,thirdColors[],uuid,uuid[],name}", + "hydra:template": "/dummy_cars{?availableAt[before],availableAt[strictly_before],availableAt[after],availableAt[strictly_after],canSell,foobar[],foobargroups[],foobargroups_override[],couleurs.prop,couleurs,couleurs[],secondColors,secondColors[],thirdColors,thirdColors[],uuid,uuid[],name}", "hydra:variableRepresentation": "BasicRepresentation", "hydra:mapping": [ { @@ -129,20 +129,20 @@ Feature: Search filter on collections }, { "@type": "IriTemplateMapping", - "variable": "colors.prop", - "property": "colors.prop", + "variable": "couleurs.prop", + "property": "couleurs.prop", "required": false }, { "@type": "IriTemplateMapping", - "variable": "colors", - "property": "colors", + "variable": "couleurs", + "property": "couleurs", "required": false }, { "@type": "IriTemplateMapping", - "variable": "colors[]", - "property": "colors", + "variable": "couleurs[]", + "property": "couleurs", "required": false }, { diff --git a/features/graphql/filters.feature b/features/graphql/filters.feature index b094006013d..c74c1733fef 100644 --- a/features/graphql/filters.feature +++ b/features/graphql/filters.feature @@ -176,8 +176,8 @@ Feature: Collections filtering } } """ - Then the JSON node "data.dummyCar.colors.edges" should have 1 element - And the JSON node "data.dummyCar.colors.edges[0].node.prop" should be equal to "blue" + Then the JSON node "data.dummyCar.couleurs.edges" should have 1 element + And the JSON node "data.dummyCar.couleurs.edges[0].node.prop" should be equal to "blue" @createSchema Scenario: Retrieve a collection filtered using the related search filter diff --git a/src/Bridge/Doctrine/Common/Filter/BooleanFilterTrait.php b/src/Bridge/Doctrine/Common/Filter/BooleanFilterTrait.php index 549b27901df..b0a61c3a9bc 100644 --- a/src/Bridge/Doctrine/Common/Filter/BooleanFilterTrait.php +++ b/src/Bridge/Doctrine/Common/Filter/BooleanFilterTrait.php @@ -50,7 +50,7 @@ public function getDescription(string $resourceClass): array if (!$this->isPropertyMapped($property, $resourceClass) || !$this->isBooleanField($property, $resourceClass)) { continue; } - $propertyName = $this->normalizePropertyName($property); + $propertyName = $this->normalizePropertyName($property, $resourceClass); $description[$propertyName] = [ 'property' => $propertyName, 'type' => 'bool', @@ -65,7 +65,7 @@ abstract protected function getProperties(): ?array; abstract protected function getLogger(): LoggerInterface; - abstract protected function normalizePropertyName($property); + abstract protected function normalizePropertyName($property/*, string $resourceClass = null*/); /** * Determines whether the given property refers to a boolean field. diff --git a/src/Bridge/Doctrine/Common/Filter/DateFilterTrait.php b/src/Bridge/Doctrine/Common/Filter/DateFilterTrait.php index 82e5db681f4..f4efea7432c 100644 --- a/src/Bridge/Doctrine/Common/Filter/DateFilterTrait.php +++ b/src/Bridge/Doctrine/Common/Filter/DateFilterTrait.php @@ -43,10 +43,10 @@ public function getDescription(string $resourceClass): array continue; } - $description += $this->getFilterDescription($property, self::PARAMETER_BEFORE); - $description += $this->getFilterDescription($property, self::PARAMETER_STRICTLY_BEFORE); - $description += $this->getFilterDescription($property, self::PARAMETER_AFTER); - $description += $this->getFilterDescription($property, self::PARAMETER_STRICTLY_AFTER); + $description += $this->getFilterDescription($property, self::PARAMETER_BEFORE, $resourceClass); + $description += $this->getFilterDescription($property, self::PARAMETER_STRICTLY_BEFORE, $resourceClass); + $description += $this->getFilterDescription($property, self::PARAMETER_AFTER, $resourceClass); + $description += $this->getFilterDescription($property, self::PARAMETER_STRICTLY_AFTER, $resourceClass); } return $description; @@ -54,7 +54,7 @@ public function getDescription(string $resourceClass): array abstract protected function getProperties(): ?array; - abstract protected function normalizePropertyName($property); + abstract protected function normalizePropertyName($property/*, string $resourceClass = null*/); /** * Determines whether the given property refers to a date field. @@ -67,9 +67,22 @@ protected function isDateField(string $property, string $resourceClass): bool /** * Gets filter description. */ - protected function getFilterDescription(string $property, string $period): array + protected function getFilterDescription(string $property, string $period/*, string $resourceClass = null*/): array { - $propertyName = $this->normalizePropertyName($property); + if (\func_num_args() > 2) { + $resourceClass = null === ($arg = func_get_arg(2)) ? $arg : (string) $arg; + } else { + if (__CLASS__ !== static::class) { + $r = new \ReflectionMethod($this, __FUNCTION__); + if (__CLASS__ !== $r->getDeclaringClass()->getName()) { + @trigger_error(sprintf('Method %s() will have a third `$resourceClass` argument in version API Platform 3.0. Not defining it is deprecated since API Platform 2.7.', __FUNCTION__), \E_USER_DEPRECATED); + } + } + + $resourceClass = null; + } + + $propertyName = $this->normalizePropertyName($property, $resourceClass); return [ sprintf('%s[%s]', $propertyName, $period) => [ diff --git a/src/Bridge/Doctrine/Common/Filter/ExistsFilterTrait.php b/src/Bridge/Doctrine/Common/Filter/ExistsFilterTrait.php index cd07e388fe4..b57769b4c8f 100644 --- a/src/Bridge/Doctrine/Common/Filter/ExistsFilterTrait.php +++ b/src/Bridge/Doctrine/Common/Filter/ExistsFilterTrait.php @@ -48,7 +48,7 @@ public function getDescription(string $resourceClass): array if (!$this->isPropertyMapped($property, $resourceClass, true) || !$this->isNullableField($property, $resourceClass)) { continue; } - $propertyName = $this->normalizePropertyName($property); + $propertyName = $this->normalizePropertyName($property, $resourceClass); $description[sprintf('%s[%s]', $this->existsParameterName, $propertyName)] = [ 'property' => $propertyName, 'type' => 'bool', @@ -68,7 +68,7 @@ abstract protected function getProperties(): ?array; abstract protected function getLogger(): LoggerInterface; - abstract protected function normalizePropertyName($property); + abstract protected function normalizePropertyName($property/*, string $resourceClass = null*/); private function normalizeValue($value, string $property): ?bool { diff --git a/src/Bridge/Doctrine/Common/Filter/NumericFilterTrait.php b/src/Bridge/Doctrine/Common/Filter/NumericFilterTrait.php index 46fd6fcebd1..6c0d6ce5a69 100644 --- a/src/Bridge/Doctrine/Common/Filter/NumericFilterTrait.php +++ b/src/Bridge/Doctrine/Common/Filter/NumericFilterTrait.php @@ -45,7 +45,7 @@ public function getDescription(string $resourceClass): array continue; } - $propertyName = $this->normalizePropertyName($property); + $propertyName = $this->normalizePropertyName($property, $resourceClass); $filterParameterNames = [$propertyName, $propertyName.'[]']; foreach ($filterParameterNames as $filterParameterName) { $description[$filterParameterName] = [ @@ -69,7 +69,7 @@ abstract protected function getProperties(): ?array; abstract protected function getLogger(): LoggerInterface; - abstract protected function normalizePropertyName($property); + abstract protected function normalizePropertyName($property/*, string $resourceClass = null*/); /** * Determines whether the given property refers to a numeric field. diff --git a/src/Bridge/Doctrine/Common/Filter/OrderFilterTrait.php b/src/Bridge/Doctrine/Common/Filter/OrderFilterTrait.php index 207f36e5e1c..d34ae3341cf 100644 --- a/src/Bridge/Doctrine/Common/Filter/OrderFilterTrait.php +++ b/src/Bridge/Doctrine/Common/Filter/OrderFilterTrait.php @@ -47,7 +47,7 @@ public function getDescription(string $resourceClass): array if (!$this->isPropertyMapped($property, $resourceClass)) { continue; } - $propertyName = $this->normalizePropertyName($property); + $propertyName = $this->normalizePropertyName($property, $resourceClass); $description[sprintf('%s[%s]', $this->orderParameterName, $propertyName)] = [ 'property' => $propertyName, 'type' => 'string', @@ -67,7 +67,7 @@ public function getDescription(string $resourceClass): array abstract protected function getProperties(): ?array; - abstract protected function normalizePropertyName($property); + abstract protected function normalizePropertyName($property/*, string $resourceClass = null*/); private function normalizeValue($value, string $property): ?string { diff --git a/src/Bridge/Doctrine/Common/Filter/RangeFilterTrait.php b/src/Bridge/Doctrine/Common/Filter/RangeFilterTrait.php index 97148b8cacc..ae4bccdae98 100644 --- a/src/Bridge/Doctrine/Common/Filter/RangeFilterTrait.php +++ b/src/Bridge/Doctrine/Common/Filter/RangeFilterTrait.php @@ -44,11 +44,11 @@ public function getDescription(string $resourceClass): array continue; } - $description += $this->getFilterDescription($property, self::PARAMETER_BETWEEN); - $description += $this->getFilterDescription($property, self::PARAMETER_GREATER_THAN); - $description += $this->getFilterDescription($property, self::PARAMETER_GREATER_THAN_OR_EQUAL); - $description += $this->getFilterDescription($property, self::PARAMETER_LESS_THAN); - $description += $this->getFilterDescription($property, self::PARAMETER_LESS_THAN_OR_EQUAL); + $description += $this->getFilterDescription($property, self::PARAMETER_BETWEEN, $resourceClass); + $description += $this->getFilterDescription($property, self::PARAMETER_GREATER_THAN, $resourceClass); + $description += $this->getFilterDescription($property, self::PARAMETER_GREATER_THAN_OR_EQUAL, $resourceClass); + $description += $this->getFilterDescription($property, self::PARAMETER_LESS_THAN, $resourceClass); + $description += $this->getFilterDescription($property, self::PARAMETER_LESS_THAN_OR_EQUAL, $resourceClass); } return $description; @@ -58,14 +58,27 @@ abstract protected function getProperties(): ?array; abstract protected function getLogger(): LoggerInterface; - abstract protected function normalizePropertyName($property); + abstract protected function normalizePropertyName($property/*, string $resourceClass = null*/); /** * Gets filter description. */ - protected function getFilterDescription(string $fieldName, string $operator): array + protected function getFilterDescription(string $property, string $operator/*, string $resourceClass = null*/): array { - $propertyName = $this->normalizePropertyName($fieldName); + if (\func_num_args() > 2) { + $resourceClass = null === ($arg = func_get_arg(2)) ? $arg : (string) $arg; + } else { + if (__CLASS__ !== static::class) { + $r = new \ReflectionMethod($this, __FUNCTION__); + if (__CLASS__ !== $r->getDeclaringClass()->getName()) { + @trigger_error(sprintf('Method %s() will have a third `$resourceClass` argument in version API Platform 3.0. Not defining it is deprecated since API Platform 2.7.', __FUNCTION__), \E_USER_DEPRECATED); + } + } + + $resourceClass = null; + } + + $propertyName = $this->normalizePropertyName($property, $resourceClass); return [ sprintf('%s[%s]', $propertyName, $operator) => [ diff --git a/src/Bridge/Doctrine/Common/Filter/SearchFilterTrait.php b/src/Bridge/Doctrine/Common/Filter/SearchFilterTrait.php index 327ef594da8..d64e3d95253 100644 --- a/src/Bridge/Doctrine/Common/Filter/SearchFilterTrait.php +++ b/src/Bridge/Doctrine/Common/Filter/SearchFilterTrait.php @@ -59,7 +59,7 @@ public function getDescription(string $resourceClass): array $metadata = $this->getClassMetadata($resourceClass); } - $propertyName = $this->normalizePropertyName($property); + $propertyName = $this->normalizePropertyName($property, $resourceClass); if ($metadata->hasField($field)) { $typeOfField = $this->getType($metadata->getTypeOfField($field)); $strategy = $this->getProperties()[$property] ?? self::STRATEGY_EXACT; @@ -112,7 +112,7 @@ abstract protected function getIriConverter(): IriConverterInterface; abstract protected function getPropertyAccessor(): PropertyAccessorInterface; - abstract protected function normalizePropertyName($property); + abstract protected function normalizePropertyName($property/*, string $resourceClass = null*/); /** * Gets the ID from an IRI or a raw ID. diff --git a/src/Bridge/Doctrine/Common/Util/PropertyNameNormalizerTrait.php b/src/Bridge/Doctrine/Common/Util/PropertyNameNormalizerTrait.php new file mode 100644 index 00000000000..9619b0fcd1e --- /dev/null +++ b/src/Bridge/Doctrine/Common/Util/PropertyNameNormalizerTrait.php @@ -0,0 +1,130 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Core\Bridge\Doctrine\Common\Util; + +use Doctrine\Persistence\Mapping\ClassMetadata; +use Symfony\Component\Serializer\NameConverter\NameConverterInterface; + +trait PropertyNameNormalizerTrait +{ + abstract protected function getNameConverter(): ?NameConverterInterface; + + abstract protected function getClassMetadata(string $resourceClass): ClassMetadata; + + /** + * @param string $property + * + * @return string + */ + protected function denormalizePropertyName($property/*, string $resourceClass, array $context = []*/) + { + if (\func_num_args() > 1) { + $resourceClass = (string) func_get_arg(1); + } else { + if (__CLASS__ !== static::class) { + $r = new \ReflectionMethod($this, __FUNCTION__); + if (__CLASS__ !== $r->getDeclaringClass()->getName()) { + @trigger_error(sprintf('Method %s() will have a second `$resourceClass` argument in version API Platform 3.0. Not defining it is deprecated since API Platform 2.7.', __FUNCTION__), \E_USER_DEPRECATED); + } + } + + $resourceClass = null; + } + + if (\func_num_args() > 2) { + $context = (array) func_get_arg(2); + } else { + if (__CLASS__ !== static::class) { + $r = new \ReflectionMethod($this, __FUNCTION__); + if (__CLASS__ !== $r->getDeclaringClass()->getName()) { + @trigger_error(sprintf('Method %s() will have a third `$context` argument in version API Platform 3.0. Not defining it is deprecated since API Platform 2.7.', __FUNCTION__), \E_USER_DEPRECATED); + } + } + + $context = []; + } + + if (!$nameConverter = $this->getNameConverter()) { + return $property; + } + + $denormalizedProperties = []; + foreach (explode('.', (string) $property) as $subProperty) { + $denormalizedProperty = $nameConverter->denormalize($subProperty, $resourceClass, null, $context); + + if (null !== $resourceClass && ($doctrineClassMetadata = $this->getClassMetadata($resourceClass))->hasAssociation($denormalizedProperty)) { + $resourceClass = $doctrineClassMetadata->getAssociationTargetClass($denormalizedProperty); + } else { + $resourceClass = null; + } + + $denormalizedProperties[] = $denormalizedProperty; + } + + return implode('.', $denormalizedProperties); + } + + /** + * @param string $property + * + * @return string + */ + protected function normalizePropertyName($property/*, string $resourceClass, array $context = []*/) + { + if (\func_num_args() > 1) { + $resourceClass = (string) func_get_arg(1); + } else { + if (__CLASS__ !== static::class) { + $r = new \ReflectionMethod($this, __FUNCTION__); + if (__CLASS__ !== $r->getDeclaringClass()->getName()) { + @trigger_error(sprintf('Method %s() will have a second `$resourceClass` argument in version API Platform 3.0. Not defining it is deprecated since API Platform 2.7.', __FUNCTION__), \E_USER_DEPRECATED); + } + } + + $resourceClass = null; + } + + if (\func_num_args() > 2) { + $context = (array) func_get_arg(2); + } else { + if (__CLASS__ !== static::class) { + $r = new \ReflectionMethod($this, __FUNCTION__); + if (__CLASS__ !== $r->getDeclaringClass()->getName()) { + @trigger_error(sprintf('Method %s() will have a third `$context` argument in version API Platform 3.0. Not defining it is deprecated since API Platform 2.7.', __FUNCTION__), \E_USER_DEPRECATED); + } + } + + $context = []; + } + + if (!$nameConverter = $this->getNameConverter()) { + return $property; + } + + $normalizedProperties = []; + foreach (explode('.', (string) $property) as $subProperty) { + $normalizedProperty = $nameConverter->normalize($subProperty, $resourceClass, null, $context); + + if (null !== $resourceClass && ($doctrineClassMetadata = $this->getClassMetadata($resourceClass))->hasAssociation($subProperty)) { + $resourceClass = $doctrineClassMetadata->getAssociationTargetClass($subProperty); + } else { + $resourceClass = null; + } + + $normalizedProperties[] = $normalizedProperty; + } + + return implode('.', $normalizedProperties); + } +} diff --git a/src/Bridge/Doctrine/MongoDbOdm/Filter/AbstractFilter.php b/src/Bridge/Doctrine/MongoDbOdm/Filter/AbstractFilter.php index e4a9bcef6d9..9c2113b3d8f 100644 --- a/src/Bridge/Doctrine/MongoDbOdm/Filter/AbstractFilter.php +++ b/src/Bridge/Doctrine/MongoDbOdm/Filter/AbstractFilter.php @@ -14,6 +14,7 @@ namespace ApiPlatform\Core\Bridge\Doctrine\MongoDbOdm\Filter; use ApiPlatform\Core\Bridge\Doctrine\Common\PropertyHelperTrait; +use ApiPlatform\Core\Bridge\Doctrine\Common\Util\PropertyNameNormalizerTrait; use ApiPlatform\Core\Bridge\Doctrine\MongoDbOdm\PropertyHelperTrait as MongoDbOdmPropertyHelperTrait; use Doctrine\ODM\MongoDB\Aggregation\Builder; use Doctrine\Persistence\ManagerRegistry; @@ -34,6 +35,7 @@ abstract class AbstractFilter implements FilterInterface { use MongoDbOdmPropertyHelperTrait; use PropertyHelperTrait; + use PropertyNameNormalizerTrait; protected $managerRegistry; protected $logger; @@ -54,7 +56,7 @@ public function __construct(ManagerRegistry $managerRegistry, LoggerInterface $l public function apply(Builder $aggregationBuilder, string $resourceClass, string $operationName = null, array &$context = []) { foreach ($context['filters'] as $property => $value) { - $this->filterProperty($this->denormalizePropertyName($property), $value, $aggregationBuilder, $resourceClass, $operationName, $context); + $this->filterProperty($this->denormalizePropertyName($property, $resourceClass, $context), $value, $aggregationBuilder, $resourceClass, $operationName, $context); } } @@ -78,6 +80,11 @@ protected function getLogger(): LoggerInterface return $this->logger; } + protected function getNameConverter(): ?NameConverterInterface + { + return $this->nameConverter; + } + /** * Determines whether the given property is enabled. */ @@ -90,22 +97,4 @@ protected function isPropertyEnabled(string $property, string $resourceClass): b return \array_key_exists($property, $this->properties); } - - protected function denormalizePropertyName($property) - { - if (!$this->nameConverter instanceof NameConverterInterface) { - return $property; - } - - return implode('.', array_map([$this->nameConverter, 'denormalize'], explode('.', (string) $property))); - } - - protected function normalizePropertyName($property) - { - if (!$this->nameConverter instanceof NameConverterInterface) { - return $property; - } - - return implode('.', array_map([$this->nameConverter, 'normalize'], explode('.', (string) $property))); - } } diff --git a/src/Bridge/Doctrine/MongoDbOdm/Filter/ExistsFilter.php b/src/Bridge/Doctrine/MongoDbOdm/Filter/ExistsFilter.php index d3624f8dcfe..30d7202c7ec 100644 --- a/src/Bridge/Doctrine/MongoDbOdm/Filter/ExistsFilter.php +++ b/src/Bridge/Doctrine/MongoDbOdm/Filter/ExistsFilter.php @@ -60,7 +60,7 @@ public function apply(Builder $aggregationBuilder, string $resourceClass, string } foreach ($context['filters'][$this->existsParameterName] as $property => $value) { - $this->filterProperty($this->denormalizePropertyName($property), $value, $aggregationBuilder, $resourceClass, $operationName, $context); + $this->filterProperty($this->denormalizePropertyName($property, $resourceClass, $context), $value, $aggregationBuilder, $resourceClass, $operationName, $context); } } diff --git a/src/Bridge/Doctrine/MongoDbOdm/Filter/OrderFilter.php b/src/Bridge/Doctrine/MongoDbOdm/Filter/OrderFilter.php index 54b18a777f9..f212e019efa 100644 --- a/src/Bridge/Doctrine/MongoDbOdm/Filter/OrderFilter.php +++ b/src/Bridge/Doctrine/MongoDbOdm/Filter/OrderFilter.php @@ -76,7 +76,7 @@ public function apply(Builder $aggregationBuilder, string $resourceClass, string } foreach ($context['filters'][$this->orderParameterName] as $property => $value) { - $this->filterProperty($this->denormalizePropertyName($property), $value, $aggregationBuilder, $resourceClass, $operationName, $context); + $this->filterProperty($this->denormalizePropertyName($property, $resourceClass, $context), $value, $aggregationBuilder, $resourceClass, $operationName, $context); } } diff --git a/src/Bridge/Doctrine/Orm/Filter/AbstractContextAwareFilter.php b/src/Bridge/Doctrine/Orm/Filter/AbstractContextAwareFilter.php index 312175040cf..9256d35f364 100644 --- a/src/Bridge/Doctrine/Orm/Filter/AbstractContextAwareFilter.php +++ b/src/Bridge/Doctrine/Orm/Filter/AbstractContextAwareFilter.php @@ -30,7 +30,7 @@ public function apply(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $q } foreach ($context['filters'] as $property => $value) { - $this->filterProperty($this->denormalizePropertyName($property), $value, $queryBuilder, $queryNameGenerator, $resourceClass, $operationName, $context); + $this->filterProperty($this->denormalizePropertyName($property, $resourceClass, $context), $value, $queryBuilder, $queryNameGenerator, $resourceClass, $operationName, $context); } } } diff --git a/src/Bridge/Doctrine/Orm/Filter/AbstractFilter.php b/src/Bridge/Doctrine/Orm/Filter/AbstractFilter.php index 5759ad3c3ce..2b791c21716 100644 --- a/src/Bridge/Doctrine/Orm/Filter/AbstractFilter.php +++ b/src/Bridge/Doctrine/Orm/Filter/AbstractFilter.php @@ -14,6 +14,7 @@ namespace ApiPlatform\Core\Bridge\Doctrine\Orm\Filter; use ApiPlatform\Core\Bridge\Doctrine\Common\PropertyHelperTrait; +use ApiPlatform\Core\Bridge\Doctrine\Common\Util\PropertyNameNormalizerTrait; use ApiPlatform\Core\Bridge\Doctrine\Orm\PropertyHelperTrait as OrmPropertyHelperTrait; use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface; use ApiPlatform\Core\Util\RequestParser; @@ -37,6 +38,7 @@ abstract class AbstractFilter implements FilterInterface { use OrmPropertyHelperTrait; use PropertyHelperTrait; + use PropertyNameNormalizerTrait; protected $managerRegistry; protected $requestStack; @@ -93,6 +95,11 @@ protected function getLogger(): LoggerInterface return $this->logger; } + protected function getNameConverter(): ?NameConverterInterface + { + return $this->nameConverter; + } + /** * Determines whether the given property is enabled. */ @@ -141,22 +148,4 @@ protected function extractProperties(Request $request/*, string $resourceClass*/ return $request->query->all(); } - - protected function denormalizePropertyName($property) - { - if (!$this->nameConverter instanceof NameConverterInterface) { - return $property; - } - - return implode('.', array_map([$this->nameConverter, 'denormalize'], explode('.', (string) $property))); - } - - protected function normalizePropertyName($property) - { - if (!$this->nameConverter instanceof NameConverterInterface) { - return $property; - } - - return implode('.', array_map([$this->nameConverter, 'normalize'], explode('.', (string) $property))); - } } diff --git a/src/Bridge/Doctrine/Orm/Filter/ExistsFilter.php b/src/Bridge/Doctrine/Orm/Filter/ExistsFilter.php index 209adbef92a..356f899f197 100644 --- a/src/Bridge/Doctrine/Orm/Filter/ExistsFilter.php +++ b/src/Bridge/Doctrine/Orm/Filter/ExistsFilter.php @@ -62,7 +62,7 @@ public function apply(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $q } foreach ($context['filters'][$this->existsParameterName] as $property => $value) { - $this->filterProperty($this->denormalizePropertyName($property), $value, $queryBuilder, $queryNameGenerator, $resourceClass, $operationName, $context); + $this->filterProperty($this->denormalizePropertyName($property, $resourceClass, $context), $value, $queryBuilder, $queryNameGenerator, $resourceClass, $operationName, $context); } } diff --git a/src/Bridge/Doctrine/Orm/Filter/OrderFilter.php b/src/Bridge/Doctrine/Orm/Filter/OrderFilter.php index e9829396abc..67c4873c815 100644 --- a/src/Bridge/Doctrine/Orm/Filter/OrderFilter.php +++ b/src/Bridge/Doctrine/Orm/Filter/OrderFilter.php @@ -77,7 +77,7 @@ public function apply(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $q } foreach ($context['filters'][$this->orderParameterName] as $property => $value) { - $this->filterProperty($this->denormalizePropertyName($property), $value, $queryBuilder, $queryNameGenerator, $resourceClass, $operationName, $context); + $this->filterProperty($this->denormalizePropertyName($property, $resourceClass, $context), $value, $queryBuilder, $queryNameGenerator, $resourceClass, $operationName, $context); } } diff --git a/tests/Fixtures/TestBundle/Entity/DummyCar.php b/tests/Fixtures/TestBundle/Entity/DummyCar.php index ad9241e0174..46d38409c69 100644 --- a/tests/Fixtures/TestBundle/Entity/DummyCar.php +++ b/tests/Fixtures/TestBundle/Entity/DummyCar.php @@ -56,6 +56,7 @@ class DummyCar * @ORM\OneToMany(targetEntity="DummyCarColor", mappedBy="car") * * @Serializer\Groups({"colors"}) + * @Serializer\SerializedName("couleurs") * @ApiFilter(SearchFilter::class, properties={"colors.prop"="ipartial", "colors"="exact"}) */ private $colors;