@@ -290,9 +290,111 @@ public function resolveResourceArgs(array $args, Operation $operation): array
290290 $ args [$ id ]['type ' ] = $ this ->typeConverter ->resolveType ($ arg ['type ' ]);
291291 }
292292
293+ /*
294+ * This is @experimental, read the comment on the parameterToObjectType function as additional information.
295+ */
296+ foreach ($ operation ->getParameters () ?? [] as $ parameter ) {
297+ $ key = $ parameter ->getKey ();
298+
299+ if (str_contains ($ key , ':property ' )) {
300+ if (!($ filterId = $ parameter ->getFilter ()) || !$ this ->filterLocator ->has ($ filterId )) {
301+ continue ;
302+ }
303+
304+ $ parsedKey = explode ('[:property] ' , $ key );
305+ $ flattenFields = [];
306+ foreach ($ this ->filterLocator ->get ($ filterId )->getDescription ($ operation ->getClass ()) as $ key => $ value ) {
307+ $ values = [];
308+ parse_str ($ key , $ values );
309+ if (isset ($ values [$ parsedKey [0 ]])) {
310+ $ values = $ values [$ parsedKey [0 ]];
311+ }
312+
313+ $ name = key ($ values );
314+ $ flattenFields [] = ['name ' => $ name , 'required ' => $ value ['required ' ] ?? null , 'description ' => $ value ['description ' ] ?? null , 'leafs ' => $ values [$ name ], 'type ' => $ value ['type ' ] ?? 'string ' ];
315+ }
316+
317+ $ args [$ parsedKey [0 ]] = $ this ->parameterToObjectType ($ flattenFields , $ parsedKey [0 ]);
318+ continue ;
319+ }
320+
321+ $ args [$ key ] = ['type ' => GraphQLType::string ()];
322+
323+ if ($ parameter ->getRequired ()) {
324+ $ args [$ key ]['type ' ] = GraphQLType::nonNull ($ args [$ key ]['type ' ]);
325+ }
326+ }
327+
293328 return $ args ;
294329 }
295330
331+ /**
332+ * Transform the result of a parse_str to a GraphQL object type.
333+ * We should consider merging getFilterArgs and this, `getFilterArgs` uses `convertType` whereas we assume that parameters have only scalar types.
334+ * Note that this method has a lower complexity then the `getFilterArgs` one.
335+ * TODO: Is there a use case with an argument being a complex type (eg: a Resource, Enum etc.)?
336+ *
337+ * @param array<array{name: string, required: bool|null, description: string|null, leafs: string|array, type: string}> $flattenFields
338+ */
339+ private function parameterToObjectType (array $ flattenFields , string $ name ): InputObjectType
340+ {
341+ $ fields = [];
342+ foreach ($ flattenFields as $ field ) {
343+ $ key = $ field ['name ' ];
344+ $ type = $ this ->getParameterType (\in_array ($ field ['type ' ], Type::$ builtinTypes , true ) ? new Type ($ field ['type ' ], !$ field ['required ' ]) : new Type ('object ' , !$ field ['required ' ], $ field ['type ' ]));
345+
346+ if (\is_array ($ l = $ field ['leafs ' ])) {
347+ if (0 === key ($ l )) {
348+ $ key = $ key ;
349+ $ type = GraphQLType::listOf ($ type );
350+ } else {
351+ $ n = [];
352+ foreach ($ field ['leafs ' ] as $ l => $ value ) {
353+ $ n [] = ['required ' => null , 'name ' => $ l , 'leafs ' => $ value , 'type ' => 'string ' , 'description ' => null ];
354+ }
355+
356+ $ type = $ this ->parameterToObjectType ($ n , $ key );
357+ if (isset ($ fields [$ key ]) && ($ t = $ fields [$ key ]['type ' ]) instanceof InputObjectType) {
358+ $ t = $ fields [$ key ]['type ' ];
359+ $ t ->config ['fields ' ] = array_merge ($ t ->config ['fields ' ], $ type ->config ['fields ' ]);
360+ $ type = $ t ;
361+ }
362+ }
363+ }
364+
365+ if ($ field ['required ' ]) {
366+ $ type = GraphQLType::nonNull ($ type );
367+ }
368+
369+ if (isset ($ fields [$ key ])) {
370+ if ($ type instanceof ListOfType) {
371+ $ key .= '_list ' ;
372+ }
373+ }
374+
375+ $ fields [$ key ] = ['type ' => $ type , 'name ' => $ key ];
376+ }
377+
378+ return new InputObjectType (['name ' => $ name , 'fields ' => $ fields ]);
379+ }
380+
381+ /**
382+ * A simplified version of convert type that does not support resources.
383+ */
384+ private function getParameterType (Type $ type ): GraphQLType
385+ {
386+ return match ($ type ->getBuiltinType ()) {
387+ Type::BUILTIN_TYPE_BOOL => GraphQLType::boolean (),
388+ Type::BUILTIN_TYPE_INT => GraphQLType::int (),
389+ Type::BUILTIN_TYPE_FLOAT => GraphQLType::float (),
390+ Type::BUILTIN_TYPE_STRING => GraphQLType::string (),
391+ Type::BUILTIN_TYPE_ARRAY => GraphQLType::listOf ($ this ->getParameterType ($ type ->getCollectionValueTypes ()[0 ])),
392+ Type::BUILTIN_TYPE_ITERABLE => GraphQLType::listOf ($ this ->getParameterType ($ type ->getCollectionValueTypes ()[0 ])),
393+ Type::BUILTIN_TYPE_OBJECT => GraphQLType::string (),
394+ default => GraphQLType::string (),
395+ };
396+ }
397+
296398 /**
297399 * Get the field configuration of a resource.
298400 *
@@ -450,9 +552,9 @@ private function getFilterArgs(array $args, ?string $resourceClass, string $root
450552 }
451553 }
452554
453- foreach ($ this ->filterLocator ->get ($ filterId )->getDescription ($ entityClass ) as $ key => $ value ) {
454- $ nullable = isset ($ value ['required ' ]) ? !$ value ['required ' ] : true ;
455- $ filterType = \in_array ($ value ['type ' ], Type::$ builtinTypes , true ) ? new Type ($ value ['type ' ], $ nullable ) : new Type ('object ' , $ nullable , $ value ['type ' ]);
555+ foreach ($ this ->filterLocator ->get ($ filterId )->getDescription ($ entityClass ) as $ key => $ description ) {
556+ $ nullable = isset ($ description ['required ' ]) ? !$ description ['required ' ] : true ;
557+ $ filterType = \in_array ($ description ['type ' ], Type::$ builtinTypes , true ) ? new Type ($ description ['type ' ], $ nullable ) : new Type ('object ' , $ nullable , $ description ['type ' ]);
456558 $ graphqlFilterType = $ this ->convertType ($ filterType , false , $ resourceOperation , $ rootOperation , $ resourceClass , $ rootResource , $ property , $ depth );
457559
458560 if (str_ends_with ($ key , '[] ' )) {
@@ -467,8 +569,8 @@ private function getFilterArgs(array $args, ?string $resourceClass, string $root
467569 if (\array_key_exists ($ key , $ parsed ) && \is_array ($ parsed [$ key ])) {
468570 $ parsed = [$ key => '' ];
469571 }
470- array_walk_recursive ($ parsed , static function (&$ value ) use ($ graphqlFilterType ): void {
471- $ value = $ graphqlFilterType ;
572+ array_walk_recursive ($ parsed , static function (&$ v ) use ($ graphqlFilterType ): void {
573+ $ v = $ graphqlFilterType ;
472574 });
473575 $ args = $ this ->mergeFilterArgs ($ args , $ parsed , $ resourceOperation , $ key );
474576 }
0 commit comments