Skip to content

Commit

Permalink
WIP: feat: add support for "@SerializedName" annotation with filters
Browse files Browse the repository at this point in the history
  • Loading branch information
meyerbaptiste committed Mar 16, 2021
1 parent 4717bd5 commit 5ff9105
Show file tree
Hide file tree
Showing 18 changed files with 223 additions and 79 deletions.
20 changes: 10 additions & 10 deletions features/doctrine/search_filter.feature
Expand Up @@ -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:
"""
Expand All @@ -29,7 +29,7 @@ Feature: Search filter on collections
{
"@id": "/dummy_cars/1",
"@type": "DummyCar",
"colors": [
"couleurs": [
{
"@id": "/dummy_car_colors/1",
"@type": "DummyCarColor",
Expand Down Expand Up @@ -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": [
{
Expand Down Expand Up @@ -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
},
{
Expand Down
4 changes: 2 additions & 2 deletions features/graphql/filters.feature
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/Bridge/Doctrine/Common/Filter/BooleanFilterTrait.php
Expand Up @@ -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',
Expand All @@ -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, array $context = []*/);

/**
* Determines whether the given property refers to a boolean field.
Expand Down
27 changes: 20 additions & 7 deletions src/Bridge/Doctrine/Common/Filter/DateFilterTrait.php
Expand Up @@ -44,18 +44,18 @@ 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;
}

abstract protected function getProperties(): ?array;

abstract protected function normalizePropertyName($property);
abstract protected function normalizePropertyName($property/*, ?string $resourceClass = null, array $context = []*/);

/**
* Determines whether the given property refers to a date field.
Expand All @@ -68,9 +68,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) => [
Expand Down
4 changes: 2 additions & 2 deletions src/Bridge/Doctrine/Common/Filter/ExistsFilterTrait.php
Expand Up @@ -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',
Expand All @@ -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, array $context = []*/);

private function normalizeValue($value, string $property): ?bool
{
Expand Down
4 changes: 2 additions & 2 deletions src/Bridge/Doctrine/Common/Filter/NumericFilterTrait.php
Expand Up @@ -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] = [
Expand All @@ -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, array $context = []*/);

/**
* Determines whether the given property refers to a numeric field.
Expand Down
4 changes: 2 additions & 2 deletions src/Bridge/Doctrine/Common/Filter/OrderFilterTrait.php
Expand Up @@ -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',
Expand All @@ -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, array $context = []*/);

private function normalizeValue($value, string $property): ?string
{
Expand Down
29 changes: 21 additions & 8 deletions src/Bridge/Doctrine/Common/Filter/RangeFilterTrait.php
Expand Up @@ -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;
Expand All @@ -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, array $context = []*/);

/**
* 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) => [
Expand Down
4 changes: 2 additions & 2 deletions src/Bridge/Doctrine/Common/Filter/SearchFilterTrait.php
Expand Up @@ -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;
Expand Down Expand Up @@ -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, array $context = []*/);

/**
* Gets the ID from an IRI or a raw ID.
Expand Down
139 changes: 139 additions & 0 deletions src/Bridge/Doctrine/Common/Util/PropertyNameNormalizerTrait.php
@@ -0,0 +1,139 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* 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\AdvancedNameConverterInterface;
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 = null, array $context = []*/)
{
if (\func_num_args() > 1) {
$resourceClass = null === ($arg = func_get_arg(1)) ? $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 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) {
if ($nameConverter instanceof AdvancedNameConverterInterface) {
$denormalizedProperty = $nameConverter->denormalize($subProperty, $resourceClass, null, $context);
} else {
$denormalizedProperty = $nameConverter->denormalize($subProperty);
}

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 = null, array $context = []*/)
{
if (\func_num_args() > 1) {
$resourceClass = null === ($arg = func_get_arg(1)) ? $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 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) {
if ($nameConverter instanceof AdvancedNameConverterInterface) {
$normalizedProperty = $nameConverter->normalize($subProperty, $resourceClass, null, $context);
} else {
$normalizedProperty = $nameConverter->normalize($subProperty);
}

if (null !== $resourceClass && ($doctrineClassMetadata = $this->getClassMetadata($resourceClass))->hasAssociation($subProperty)) {
$resourceClass = $doctrineClassMetadata->getAssociationTargetClass($subProperty);
} else {
$resourceClass = null;
}

$normalizedProperties[] = $normalizedProperty;
}

return implode('.', $normalizedProperties);
}
}

0 comments on commit 5ff9105

Please sign in to comment.