2020use ApiPlatform \Metadata \GraphQl \Mutation ;
2121use ApiPlatform \Metadata \GraphQl \Operation ;
2222use ApiPlatform \Metadata \GraphQl \Query ;
23- use ApiPlatform \Metadata \GraphQl \QueryCollection ;
2423use ApiPlatform \Metadata \GraphQl \Subscription ;
25- use ApiPlatform \Metadata \Operation as AbstractOperation ;
2624use ApiPlatform \Metadata \Property \Factory \PropertyMetadataFactoryInterface ;
2725use ApiPlatform \Metadata \Property \Factory \PropertyNameCollectionFactoryInterface ;
2826use ApiPlatform \Metadata \Resource \Factory \ResourceMetadataCollectionFactoryInterface ;
4745 */
4846final class FieldsBuilder implements FieldsBuilderInterface
4947{
50- public function __construct (private readonly PropertyNameCollectionFactoryInterface $ propertyNameCollectionFactory , private readonly PropertyMetadataFactoryInterface $ propertyMetadataFactory , private readonly ResourceMetadataCollectionFactoryInterface $ resourceMetadataCollectionFactory , private readonly ResourceClassResolverInterface $ resourceClassResolver , private readonly TypesContainerInterface $ typesContainer , private readonly TypeBuilderInterface $ typeBuilder , private readonly TypeConverterInterface $ typeConverter , private readonly ResolverFactoryInterface $ itemResolverFactory , private readonly ResolverFactoryInterface $ collectionResolverFactory , private readonly ResolverFactoryInterface $ itemMutationResolverFactory , private readonly ResolverFactoryInterface $ itemSubscriptionResolverFactory , private readonly ContainerInterface $ filterLocator , private readonly Pagination $ pagination , private readonly ?NameConverterInterface $ nameConverter , private readonly string $ nestingSeparator )
48+ public function __construct (private readonly PropertyNameCollectionFactoryInterface $ propertyNameCollectionFactory , private readonly PropertyMetadataFactoryInterface $ propertyMetadataFactory , private readonly ResourceMetadataCollectionFactoryInterface $ resourceMetadataCollectionFactory , private readonly ResourceClassResolverInterface $ resourceClassResolver , private readonly TypesContainerInterface $ typesContainer , private readonly TypeBuilderInterface $ typeBuilder , private readonly TypeConverterInterface $ typeConverter , private readonly ResolverFactoryInterface $ itemResolverFactory , private readonly ResolverFactoryInterface $ collectionResolverFactory , private readonly ResolverFactoryInterface $ itemMutationResolverFactory , private readonly ResolverFactoryInterface $ itemSubscriptionResolverFactory , private readonly ContainerInterface $ filterLocator , private readonly Pagination $ pagination , private readonly ?NameConverterInterface $ nameConverter , private readonly string $ nestingSeparator , private readonly ?ResourceMetadataCollectionFactoryInterface $ graphQlNestedOperationResourceMetadataFactory = null )
5149 {
5250 }
5351
@@ -256,7 +254,23 @@ private function getResourceFieldConfiguration(?string $property, ?string $field
256254 $ resourceClass = $ type ->getClassName ();
257255 }
258256
259- $ graphqlType = $ this ->convertType ($ type , $ input , $ rootOperation , $ resourceClass ?? '' , $ rootResource , $ property , $ depth , $ forceNullable );
257+ $ resourceOperation = $ rootOperation ;
258+ if ($ resourceClass && $ rootOperation ->getClass () && $ this ->resourceClassResolver ->isResourceClass ($ resourceClass ) && $ rootOperation ->getClass () !== $ resourceClass ) {
259+ $ resourceMetadataCollection = $ this ->resourceMetadataCollectionFactory ->create ($ resourceClass );
260+ try {
261+ $ resourceOperation = $ resourceMetadataCollection ->getOperation ($ isCollectionType ? 'collection_query ' : 'item_query ' );
262+ } catch (OperationNotFoundException ) {
263+ // If there is no query operation for a nested resource we force one to exist
264+ $ nestedResourceMetadataCollection = $ this ->graphQlNestedOperationResourceMetadataFactory ->create ($ resourceClass );
265+ $ resourceOperation = $ nestedResourceMetadataCollection ->getOperation ($ isCollectionType ? 'collection_query ' : 'item_query ' );
266+ }
267+ }
268+
269+ if (!$ resourceOperation instanceof Operation) {
270+ throw new \LogicException ('The resource operation should be a GraphQL operation. ' );
271+ }
272+
273+ $ graphqlType = $ this ->convertType ($ type , $ input , $ resourceOperation , $ rootOperation , $ resourceClass ?? '' , $ rootResource , $ property , $ depth , $ forceNullable );
260274
261275 $ graphqlWrappedType = $ graphqlType instanceof WrappingType ? $ graphqlType ->getWrappedType (true ) : $ graphqlType ;
262276 $ isStandardGraphqlType = \in_array ($ graphqlWrappedType , GraphQLType::getStandardTypes (), true );
@@ -271,43 +285,22 @@ private function getResourceFieldConfiguration(?string $property, ?string $field
271285
272286 $ args = [];
273287
274- $ resolverOperation = $ rootOperation ;
275-
276- if ($ resourceClass && $ this ->resourceClassResolver ->isResourceClass ($ resourceClass ) && $ rootOperation ->getClass () !== $ resourceClass ) {
277- $ resourceMetadataCollection = $ this ->resourceMetadataCollectionFactory ->create ($ resourceClass );
278- $ resolverOperation = $ resourceMetadataCollection ->getOperation (null , $ isCollectionType );
279-
280- if (!$ resolverOperation instanceof Operation) {
281- $ resolverOperation = ($ isCollectionType ? new QueryCollection () : new Query ())->withOperation ($ resolverOperation );
282- }
283- }
284-
285288 if (!$ input && !$ rootOperation instanceof Mutation && !$ rootOperation instanceof Subscription && !$ isStandardGraphqlType && $ isCollectionType ) {
286- if ($ this ->pagination ->isGraphQlEnabled ($ rootOperation )) {
287- $ args = $ this ->getGraphQlPaginationArgs ($ rootOperation );
288- }
289-
290- // Find the collection operation to get filters, there might be a smarter way to do this
291- $ operation = null ;
292- if (!empty ($ resourceClass )) {
293- $ resourceMetadataCollection = $ this ->resourceMetadataCollectionFactory ->create ($ resourceClass );
294- try {
295- $ operation = $ resourceMetadataCollection ->getOperation (null , true );
296- } catch (OperationNotFoundException ) {
297- }
289+ if ($ this ->pagination ->isGraphQlEnabled ($ resourceOperation )) {
290+ $ args = $ this ->getGraphQlPaginationArgs ($ resourceOperation );
298291 }
299292
300- $ args = $ this ->getFilterArgs ($ args , $ resourceClass , $ rootResource , $ rootOperation , $ property , $ depth , $ operation );
293+ $ args = $ this ->getFilterArgs ($ args , $ resourceClass , $ rootResource , $ resourceOperation , $ rootOperation , $ property , $ depth );
301294 }
302295
303296 if ($ isStandardGraphqlType || $ input ) {
304297 $ resolve = null ;
305298 } elseif (($ rootOperation instanceof Mutation || $ rootOperation instanceof Subscription) && $ depth <= 0 ) {
306- $ resolve = $ rootOperation instanceof Mutation ? ($ this ->itemMutationResolverFactory )($ resourceClass , $ rootResource , $ resolverOperation ) : ($ this ->itemSubscriptionResolverFactory )($ resourceClass , $ rootResource , $ resolverOperation );
299+ $ resolve = $ rootOperation instanceof Mutation ? ($ this ->itemMutationResolverFactory )($ resourceClass , $ rootResource , $ resourceOperation ) : ($ this ->itemSubscriptionResolverFactory )($ resourceClass , $ rootResource , $ resourceOperation );
307300 } elseif ($ this ->typeBuilder ->isCollection ($ type )) {
308- $ resolve = ($ this ->collectionResolverFactory )($ resourceClass , $ rootResource , $ resolverOperation );
301+ $ resolve = ($ this ->collectionResolverFactory )($ resourceClass , $ rootResource , $ resourceOperation );
309302 } else {
310- $ resolve = ($ this ->itemResolverFactory )($ resourceClass , $ rootResource , $ resolverOperation );
303+ $ resolve = ($ this ->itemResolverFactory )($ resourceClass , $ rootResource , $ resourceOperation );
311304 }
312305
313306 return [
@@ -368,21 +361,21 @@ private function getGraphQlPaginationArgs(Operation $queryOperation): array
368361 return $ args ;
369362 }
370363
371- private function getFilterArgs (array $ args , ?string $ resourceClass , string $ rootResource , Operation $ rootOperation , ?string $ property , int $ depth, ? AbstractOperation $ operation = null ): array
364+ private function getFilterArgs (array $ args , ?string $ resourceClass , string $ rootResource , Operation $ resourceOperation , Operation $ rootOperation , ?string $ property , int $ depth ): array
372365 {
373- if (null === $ operation || null === $ resourceClass ) {
366+ if (null === $ resourceClass ) {
374367 return $ args ;
375368 }
376369
377- foreach ($ operation ->getFilters () ?? [] as $ filterId ) {
370+ foreach ($ resourceOperation ->getFilters () ?? [] as $ filterId ) {
378371 if (!$ this ->filterLocator ->has ($ filterId )) {
379372 continue ;
380373 }
381374
382375 foreach ($ this ->filterLocator ->get ($ filterId )->getDescription ($ resourceClass ) as $ key => $ value ) {
383376 $ nullable = isset ($ value ['required ' ]) ? !$ value ['required ' ] : true ;
384377 $ filterType = \in_array ($ value ['type ' ], Type::$ builtinTypes , true ) ? new Type ($ value ['type ' ], $ nullable ) : new Type ('object ' , $ nullable , $ value ['type ' ]);
385- $ graphqlFilterType = $ this ->convertType ($ filterType , false , $ rootOperation , $ resourceClass , $ rootResource , $ property , $ depth );
378+ $ graphqlFilterType = $ this ->convertType ($ filterType , false , $ resourceOperation , $ rootOperation , $ resourceClass , $ rootResource , $ property , $ depth );
386379
387380 if (str_ends_with ($ key , '[] ' )) {
388381 $ graphqlFilterType = GraphQLType::listOf ($ graphqlFilterType );
@@ -399,14 +392,14 @@ private function getFilterArgs(array $args, ?string $resourceClass, string $root
399392 array_walk_recursive ($ parsed , static function (&$ value ) use ($ graphqlFilterType ): void {
400393 $ value = $ graphqlFilterType ;
401394 });
402- $ args = $ this ->mergeFilterArgs ($ args , $ parsed , $ operation , $ key );
395+ $ args = $ this ->mergeFilterArgs ($ args , $ parsed , $ resourceOperation , $ key );
403396 }
404397 }
405398
406399 return $ this ->convertFilterArgsToTypes ($ args );
407400 }
408401
409- private function mergeFilterArgs (array $ args , array $ parsed , ?AbstractOperation $ operation = null , string $ original = '' ): array
402+ private function mergeFilterArgs (array $ args , array $ parsed , ?Operation $ operation = null , string $ original = '' ): array
410403 {
411404 foreach ($ parsed as $ key => $ value ) {
412405 // Never override keys that cannot be merged
@@ -470,7 +463,7 @@ private function convertFilterArgsToTypes(array $args): array
470463 *
471464 * @throws InvalidTypeException
472465 */
473- private function convertType (Type $ type , bool $ input , Operation $ rootOperation , string $ resourceClass , string $ rootResource , ?string $ property , int $ depth , bool $ forceNullable = false ): GraphQLType |ListOfType |NonNull
466+ private function convertType (Type $ type , bool $ input , Operation $ resourceOperation , Operation $ rootOperation , string $ resourceClass , string $ rootResource , ?string $ property , int $ depth , bool $ forceNullable = false ): GraphQLType |ListOfType |NonNull
474467 {
475468 $ graphqlType = $ this ->typeConverter ->convertType ($ type , $ input , $ rootOperation , $ resourceClass , $ rootResource , $ property , $ depth );
476469
@@ -487,7 +480,7 @@ private function convertType(Type $type, bool $input, Operation $rootOperation,
487480 }
488481
489482 if ($ this ->typeBuilder ->isCollection ($ type )) {
490- return $ this ->pagination ->isGraphQlEnabled ($ rootOperation ) && !$ input ? $ this ->typeBuilder ->getResourcePaginatedCollectionType ($ graphqlType , $ resourceClass , $ rootOperation ) : GraphQLType::listOf ($ graphqlType );
483+ return $ this ->pagination ->isGraphQlEnabled ($ resourceOperation ) && !$ input ? $ this ->typeBuilder ->getResourcePaginatedCollectionType ($ graphqlType , $ resourceClass , $ resourceOperation ) : GraphQLType::listOf ($ graphqlType );
491484 }
492485
493486 return $ forceNullable || !$ graphqlType instanceof NullableType || $ type ->isNullable () || ($ rootOperation instanceof Mutation && 'update ' === $ rootOperation ->getName ())
0 commit comments