Skip to content

Commit 78457bb

Browse files
committed
Allow ordering on nested property values
1 parent 76cfbf1 commit 78457bb

File tree

4 files changed

+102
-13
lines changed

4 files changed

+102
-13
lines changed

features/doctrine/order_filter.feature

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,3 +830,55 @@ Feature: Order filter on collections
830830
}
831831
}
832832
"""
833+
834+
@createSchema
835+
Scenario: Get collection ordered in descending order on a related property
836+
Given there are 2 dummy objects with relatedDummy
837+
When I send a "GET" request to "/dummies?order[relatedDummy.name]=desc"
838+
Then the response status code should be 200
839+
And the response should be in JSON
840+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
841+
And the JSON should be valid according to this schema:
842+
"""
843+
{
844+
"type": "object",
845+
"properties": {
846+
"@context": {"pattern": "^/contexts/Dummy$"},
847+
"@id": {"pattern": "^/dummies$"},
848+
"@type": {"pattern": "^hydra:Collection$"},
849+
"hydra:member": {
850+
"type": "array",
851+
"items": [
852+
{
853+
"type": "object",
854+
"properties": {
855+
"@id": {
856+
"type": "string",
857+
"pattern": "^/dummies/2$"
858+
}
859+
}
860+
},
861+
{
862+
"type": "object",
863+
"properties": {
864+
"@id": {
865+
"type": "string",
866+
"pattern": "^/dummies/1$"
867+
}
868+
}
869+
}
870+
],
871+
"additionalItems": false,
872+
"maxItems": 2,
873+
"minItems": 2
874+
},
875+
"hydra:view": {
876+
"type": "object",
877+
"properties": {
878+
"@id": {"pattern": "^/dummies\\?order%5BrelatedDummy.name%5D=desc"},
879+
"@type": {"pattern": "^hydra:PartialCollectionView$"}
880+
}
881+
}
882+
}
883+
}
884+
"""

features/graphql/filters.feature

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,6 @@ Feature: Collections filtering
9393
Scenario: Retrieve a collection filtered using the related search filter
9494
Given there are 1 dummy objects having each 2 relatedDummies
9595
And there are 1 dummy objects having each 3 relatedDummies
96-
When I add "Accept" header equal to "application/hal+json"
97-
And I send a "GET" request to "/dummies?relatedDummies.name=RelatedDummy31"
98-
Then the response status code should be 200
99-
And the response should be in JSON
100-
And the JSON node "_embedded.item" should have 1 element
10196
When I send the following GraphQL request:
10297
"""
10398
{
@@ -112,3 +107,27 @@ Feature: Collections filtering
112107
"""
113108
And the response status code should be 200
114109
And the JSON node "data.dummies.edges" should have 1 element
110+
111+
@createSchema
112+
Scenario: Retrieve a collection ordered using nested properties
113+
Given there are 2 dummy objects with relatedDummy
114+
When I send the following GraphQL request:
115+
"""
116+
{
117+
dummies(order: {relatedDummy_name: "DESC"}) {
118+
edges {
119+
node {
120+
name
121+
relatedDummy {
122+
id
123+
name
124+
}
125+
}
126+
}
127+
}
128+
}
129+
"""
130+
Then the response status code should be 200
131+
And the header "Content-Type" should be equal to "application/json"
132+
And the JSON node "data.dummies.edges[0].node.name" should be equal to "Dummy #2"
133+
And the JSON node "data.dummies.edges[1].node.name" should be equal to "Dummy #1"

src/GraphQl/Resolver/Factory/CollectionResolverFactory.php

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,7 @@ public function __invoke(string $resourceClass = null, string $rootClass = null,
7777
$resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
7878
$dataProviderContext = $resourceMetadata->getGraphqlAttribute('query', 'normalization_context', [], true);
7979
$dataProviderContext['attributes'] = $this->fieldsToAttributes($info);
80-
$filters = $args;
81-
foreach ($filters as $name => $value) {
82-
if (strpos($name, '_')) {
83-
// Gives a chance to relations/nested fields
84-
$filters[str_replace('_', '.', $name)] = $value;
85-
}
86-
}
87-
$dataProviderContext['filters'] = $filters;
80+
$dataProviderContext['filters'] = $this->getNormalizedFilters($args);
8881

8982
if (isset($rootClass, $source[$rootProperty = $info->fieldName], $source[ItemNormalizer::ITEM_KEY])) {
9083
$rootResolvedFields = $this->identifiersExtractor->getIdentifiersFromItem(unserialize($source[ItemNormalizer::ITEM_KEY]));
@@ -171,4 +164,22 @@ private function getSubresource(string $rootClass, array $rootResolvedFields, ar
171164
'collection' => $isCollection,
172165
]);
173166
}
167+
168+
private function getNormalizedFilters(array $args): array
169+
{
170+
$filters = $args;
171+
foreach ($filters as $name => $value) {
172+
if (\is_array($value)) {
173+
$filters[$name] = $this->getNormalizedFilters($value);
174+
continue;
175+
}
176+
177+
if (strpos($name, '_')) {
178+
// Gives a chance to relations/nested fields.
179+
$filters[str_replace('_', '.', $name)] = $value;
180+
}
181+
}
182+
183+
return $filters;
184+
}
174185
}

src/GraphQl/Type/SchemaBuilder.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,13 @@ private function mergeFilterArgs(array $args, array $parsed, ResourceMetadata $r
291291

292292
private function convertFilterArgsToTypes(array $args): array
293293
{
294+
foreach ($args as $key => $value) {
295+
if (strpos($key, '.')) {
296+
// Declare relations/nested fields in a GraphQL compatible syntax.
297+
$args[str_replace('.', '_', $key)] = $value;
298+
}
299+
}
300+
294301
foreach ($args as $key => $value) {
295302
if (!\is_array($value) || !isset($value['#name'])) {
296303
continue;

0 commit comments

Comments
 (0)