@@ -311,6 +311,60 @@ public function resolveResourceArgs(array $args, Operation $operation): array
311311 return $ args ;
312312 }
313313
314+ /**
315+ * Transform the result of a parse_str to a GraphQL object type.
316+ * We should consider merging getFilterArgs and this, `getFilterArgs` uses `convertType` whereas we assume that parameters have only scalar types.
317+ * Note that this method has a lower complexity then the `getFilterArgs` one.
318+ * TODO: Is there a use case with an argument being a complex type (eg: a Resource, Enum etc.)?
319+ *
320+ * @param array<array{name: string, required: bool|null, description: string|null, leafs: string|array, type: string}> $flattenFields
321+ */
322+ private function parameterToObjectType (array $ flattenFields , string $ name ): InputObjectType
323+ {
324+ $ fields = [];
325+ foreach ($ flattenFields as $ field ) {
326+ $ key = $ field ['name ' ];
327+ $ type = \in_array ($ field ['type ' ], TypeIdentifier::values (), true ) ? Type::builtin ($ field ['type ' ]) : Type::object ($ field ['type ' ]);
328+ if (!$ field ['required ' ]) {
329+ $ type = Type::nullable ($ type );
330+ }
331+
332+ $ type = $ this ->getParameterType ($ type );
333+ if (\is_array ($ l = $ field ['leafs ' ])) {
334+ if (0 === key ($ l )) {
335+ $ key = $ key ;
336+ $ type = GraphQLType::listOf ($ type );
337+ } else {
338+ $ n = [];
339+ foreach ($ field ['leafs ' ] as $ l => $ value ) {
340+ $ n [] = ['required ' => null , 'name ' => $ l , 'leafs ' => $ value , 'type ' => 'string ' , 'description ' => null ];
341+ }
342+
343+ $ type = $ this ->parameterToObjectType ($ n , $ key );
344+ if (isset ($ fields [$ key ]) && ($ t = $ fields [$ key ]['type ' ]) instanceof InputObjectType) {
345+ $ t = $ fields [$ key ]['type ' ];
346+ $ t ->config ['fields ' ] = array_merge ($ t ->config ['fields ' ], $ type ->config ['fields ' ]);
347+ $ type = $ t ;
348+ }
349+ }
350+ }
351+
352+ if ($ field ['required ' ]) {
353+ $ type = GraphQLType::nonNull ($ type );
354+ }
355+
356+ if (isset ($ fields [$ key ])) {
357+ if ($ type instanceof ListOfType) {
358+ $ key .= '_list ' ;
359+ }
360+ }
361+
362+ $ fields [$ key ] = ['type ' => $ type , 'name ' => $ key ];
363+ }
364+
365+ return new InputObjectType (['name ' => $ name , 'fields ' => $ fields ]);
366+ }
367+
314368 /**
315369 * A simplified version of convert type that does not support resources.
316370 */
@@ -445,92 +499,57 @@ private function getParameterArgs(Operation $operation, array $args = []): array
445499 {
446500 foreach ($ operation ->getParameters () ?? [] as $ parameter ) {
447501 $ key = $ parameter ->getKey ();
448- $ property = $ parameter ->getProperty ();
449-
450- $ matchFound = false ;
451-
452- if ($ filter = $ this ->getFilterInstance ($ parameter ->getFilter ())) {
453- foreach ($ filter ->getDescription ($ operation ->getClass ()) as $ name => $ value ) {
454- // Check if this description entry matches the current parameter's property
455- if ($ property && ($ value ['property ' ] ?? null ) === $ property ) {
456- $ matchFound = true ;
457-
458- $ suffix = '' ;
459- if (str_starts_with ($ name , $ property )) {
460- $ suffix = substr ($ name , \strlen ($ property ));
461- }
462-
463- $ argName = $ key .$ suffix ;
464502
465- $ type = \in_array ($ value ['type ' ] ?? 'string ' , TypeIdentifier::values (), true ) ? Type::builtin ($ value ['type ' ] ?? 'string ' ) : Type::object ($ value ['type ' ] ?? 'string ' );
503+ if (!str_contains ($ key , ':property ' )) {
504+ $ args [$ key ] = ['type ' => GraphQLType::string ()];
466505
467- if (!($ value ['required ' ] ?? false )) {
468- $ type = Type::nullable ($ type );
469- }
470-
471- $ graphQlType = $ this ->getParameterType ($ type );
506+ if ($ parameter ->getRequired ()) {
507+ $ args [$ key ]['type ' ] = GraphQLType::nonNull ($ args [$ key ]['type ' ]);
508+ }
472509
473- parse_str ($ argName , $ parsed );
474- array_walk_recursive ($ parsed , static function (&$ v ) use ($ graphQlType ): void {
475- $ v = $ graphQlType ;
476- });
510+ continue ;
511+ }
477512
478- $ args = $ this -> mergeFilterArgs ( $ args , $ parsed , $ operation , $ key );
479- }
480- }
513+ if (!( $ filterId = $ parameter -> getFilter ()) || ! $ this -> filterLocator -> has ( $ filterId )) {
514+ continue ;
515+ }
481516
482- if ($ filter instanceof OpenApiParameterFilterInterface) {
483- foreach ($ filter ->getOpenApiParameters ($ parameter ) as $ value ) {
484- $ matchFound = true ;
485- $ suffix = '' ;
486- if ($ property && str_starts_with ($ value ->getName (), $ property )) {
487- $ suffix = substr ($ value ->getName (), \strlen ($ property ));
488- }
517+ $ filter = $ this ->filterLocator ->get ($ filterId );
518+ $ parsedKey = explode ('[:property] ' , $ key );
519+ $ flattenFields = [];
489520
490- $ argName = $ key .$ suffix ;
491- $ type = \in_array ($ value ->getSchema ()['type ' ] ?? 'string ' , TypeIdentifier::values (), true ) ? Type::builtin ($ value ->getSchema ()['type ' ] ?? 'string ' ) : Type::object ($ value ->getSchema ()['type ' ] ?? 'string ' );
492- if (!$ value ->getRequired ()) {
493- $ type = Type::nullable ($ type );
494- }
495- $ graphQlType = $ this ->getParameterType ($ type );
496- parse_str ($ argName , $ parsed );
497- array_walk_recursive ($ parsed , static function (&$ v ) use ($ graphQlType ): void {
498- $ v = $ graphQlType ;
499- });
500- $ args = $ this ->mergeFilterArgs ($ args , $ parsed , $ operation , $ key );
521+ if ($ filter instanceof FilterInterface) {
522+ foreach ($ filter ->getDescription ($ operation ->getClass ()) as $ name => $ value ) {
523+ $ values = [];
524+ parse_str ($ name , $ values );
525+ if (isset ($ values [$ parsedKey [0 ]])) {
526+ $ values = $ values [$ parsedKey [0 ]];
501527 }
502- }
503- }
504528
505- if (!$ matchFound ) {
506- $ type = GraphQLType::string ();
507- if ($ parameter ->getNativeType ()) {
508- $ type = $ this ->getParameterType ($ parameter ->getNativeType ());
529+ $ name = key ($ values );
530+ $ flattenFields [] = ['name ' => $ name , 'required ' => $ value ['required ' ] ?? null , 'description ' => $ value ['description ' ] ?? null , 'leafs ' => $ values [$ name ], 'type ' => $ value ['type ' ] ?? 'string ' ];
509531 }
510532
511- $ arg = ['type ' => $ type ];
533+ $ args [$ parsedKey [0 ]] = $ this ->parameterToObjectType ($ flattenFields , $ parsedKey [0 ]);
534+ }
512535
513- if ($ parameter ->getRequired ()) {
514- $ arg ['type ' ] = GraphQLType::nonNull ($ arg ['type ' ]);
515- }
536+ if ($ filter instanceof OpenApiParameterFilterInterface) {
537+ foreach ($ filter ->getOpenApiParameters ($ parameter ) as $ value ) {
538+ $ values = [];
539+ parse_str ($ value ->getName (), $ values );
540+ if (isset ($ values [$ parsedKey [0 ]])) {
541+ $ values = $ values [$ parsedKey [0 ]];
542+ }
516543
517- if ( $ parameter -> getDescription ()) {
518- $ arg [ ' description '] = $ parameter ->getDescription ();
544+ $ name = key ( $ values );
545+ $ flattenFields [] = [ ' name ' => $ name , ' required ' => $ value -> getRequired (), ' description ' => $ value ->getDescription (), ' leafs ' => $ values [ $ name ], ' type ' => $ value -> getSchema ()[ ' type ' ] ?? ' string ' ] ;
519546 }
520547
521- if (str_contains ($ key , '[ ' )) {
522- parse_str ($ key , $ parsed );
523- array_walk_recursive ($ parsed , static function (&$ v ) use ($ arg ): void {
524- $ v = $ arg ['type ' ];
525- });
526- $ args = $ this ->mergeFilterArgs ($ args , $ parsed , $ operation , $ key );
527- } else {
528- $ args [$ key ] = $ arg ;
529- }
548+ $ args [$ parsedKey [0 ]] = $ this ->parameterToObjectType ($ flattenFields , $ parsedKey [0 ].$ operation ->getShortName ().$ operation ->getName ());
530549 }
531550 }
532551
533- return $ this -> convertFilterArgsToTypes ( $ args) ;
552+ return $ args ;
534553 }
535554
536555 private function getGraphQlPaginationArgs (Operation $ queryOperation ): array
@@ -627,10 +646,10 @@ private function mergeFilterArgs(array $args, array $parsed, ?Operation $operati
627646 }
628647
629648 if (\is_array ($ value )) {
630- $ value = $ this ->mergeFilterArgs ($ args [$ key ] ?? [], $ value, $ operation , $ original );
649+ $ value = $ this ->mergeFilterArgs ($ args [$ key ] ?? [], $ value );
631650 if (!isset ($ value ['#name ' ])) {
632651 $ name = (false === $ pos = strrpos ($ original , '[ ' )) ? $ original : substr ($ original , 0 , (int ) $ pos );
633- $ value ['#name ' ] = $ name . ($ operation ? $ operation ->getShortName (). $ operation -> getName () : '' );
652+ $ value ['#name ' ] = ($ operation ? $ operation ->getShortName () : '' ). ' Filter_ ' . strtr ( $ name , [ ' [ ' => ' _ ' , ' ] ' => '' , ' . ' => ' __ ' ] );
634653 }
635654 }
636655
@@ -664,10 +683,10 @@ private function convertFilterArgsToTypes(array $args): array
664683
665684 unset($ value ['#name ' ]);
666685
667- $ filterArgType = new InputObjectType ([
686+ $ filterArgType = GraphQLType:: listOf ( new InputObjectType ([
668687 'name ' => $ name ,
669688 'fields ' => $ this ->convertFilterArgsToTypes ($ value ),
670- ]);
689+ ])) ;
671690
672691 $ this ->typesContainer ->set ($ name , $ filterArgType );
673692
@@ -723,27 +742,5 @@ private function normalizePropertyName(string $property, string $resourceClass):
723742
724743 return $ this ->nameConverter ->normalize ($ property , $ resourceClass );
725744 }
726-
727- private function getFilterInstance (object |string |null $ filter ): ?FilterInterface
728- {
729- if (!$ filter ) {
730- return null ;
731- }
732-
733- if (\is_object ($ filter )) {
734- return $ filter instanceof FilterInterface ? $ filter : null ;
735- }
736-
737- if (!$ this ->filterLocator ->has ($ filter )) {
738- return null ;
739- }
740-
741- $ filter = $ this ->filterLocator ->get ($ filter );
742-
743- if (!$ filter instanceof FilterInterface) {
744- return null ;
745- }
746-
747- return $ filter ;
748- }
749745}
746+
0 commit comments