Skip to content

Commit fee0d3f

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

File tree

4 files changed

+98
-9
lines changed

4 files changed

+98
-9
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 & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@ Feature: Collections filtering
9696
When I add "Accept" header equal to "application/hal+json"
9797
And I send a "GET" request to "/dummies?relatedDummies.name=RelatedDummy31"
9898
Then the response status code should be 200
99-
And the response should be in JSON
10099
And the JSON node "_embedded.item" should have 1 element
101100
When I send the following GraphQL request:
102101
"""
@@ -112,3 +111,27 @@ Feature: Collections filtering
112111
"""
113112
And the response status code should be 200
114113
And the JSON node "data.dummies.edges" should have 1 element
114+
115+
@createSchema
116+
Scenario: Retrieve a collection ordered using nested properties
117+
Given there are 2 dummy objects with relatedDummy
118+
When I send the following GraphQL request:
119+
"""
120+
{
121+
dummies(order: {relatedDummy_name: "DESC"}) {
122+
edges {
123+
node {
124+
name
125+
relatedDummy {
126+
id
127+
name
128+
}
129+
}
130+
}
131+
}
132+
}
133+
"""
134+
Then the response status code should be 200
135+
And the header "Content-Type" should be equal to "application/json"
136+
And the JSON node "data.dummies.edges[0].node.name" should be equal to "Dummy #2"
137+
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($args)
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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,9 @@ private function mergeFilterArgs(array $args, array $parsed, ResourceMetadata $r
292292
private function convertFilterArgsToTypes(array $args): array
293293
{
294294
foreach ($args as $key => $value) {
295+
if (strpos($key, '.')) {
296+
$args[str_replace('.', '_', $key)] = $value;
297+
}
295298
if (!\is_array($value) || !isset($value['#name'])) {
296299
continue;
297300
}

0 commit comments

Comments
 (0)