17
17
use ApiPlatform \Exception \OperationNotFoundException ;
18
18
use ApiPlatform \GraphQl \Resolver \Factory \ResolverFactoryInterface ;
19
19
use ApiPlatform \GraphQl \Type \Definition \TypeInterface ;
20
- use ApiPlatform \Metadata \Extractor \DynamicResourceExtractorInterface ;
21
20
use ApiPlatform \Metadata \GraphQl \Mutation ;
22
21
use ApiPlatform \Metadata \GraphQl \Operation ;
22
+ use ApiPlatform \Metadata \GraphQl \Query ;
23
+ use ApiPlatform \Metadata \GraphQl \QueryCollection ;
23
24
use ApiPlatform \Metadata \GraphQl \Subscription ;
25
+ use ApiPlatform \Metadata \Operation as AbstractOperation ;
24
26
use ApiPlatform \Metadata \Property \Factory \PropertyMetadataFactoryInterface ;
25
27
use ApiPlatform \Metadata \Property \Factory \PropertyNameCollectionFactoryInterface ;
26
28
use ApiPlatform \Metadata \Resource \Factory \ResourceMetadataCollectionFactoryInterface ;
45
47
*/
46
48
final class FieldsBuilder implements FieldsBuilderInterface
47
49
{
48
- public function __construct (private readonly PropertyNameCollectionFactoryInterface $ propertyNameCollectionFactory , private readonly PropertyMetadataFactoryInterface $ propertyMetadataFactory , private readonly ResourceMetadataCollectionFactoryInterface $ resourceMetadataCollectionFactory , private readonly DynamicResourceExtractorInterface $ dynamicResourceExtractor , 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 )
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 )
49
51
{
50
52
}
51
53
@@ -254,25 +256,7 @@ private function getResourceFieldConfiguration(?string $property, ?string $field
254
256
$ resourceClass = $ type ->getClassName ();
255
257
}
256
258
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, use a dynamic resource to get one.
264
- $ dynamicResourceMetadataCollection = $ this ->resourceMetadataCollectionFactory ->create ($ this ->dynamicResourceExtractor ->addResource ($ resourceClass ));
265
-
266
- $ resourceOperation = $ dynamicResourceMetadataCollection ->getOperation ($ isCollectionType ? 'collection_query ' : 'item_query ' )
267
- ->withResource ($ resourceMetadataCollection [0 ]);
268
- }
269
- }
270
-
271
- if (!$ resourceOperation instanceof Operation) {
272
- throw new \LogicException ('The resource operation should be a GraphQL operation. ' );
273
- }
274
-
275
- $ graphqlType = $ this ->convertType ($ type , $ input , $ resourceOperation , $ rootOperation , $ resourceClass ?? '' , $ rootResource , $ property , $ depth , $ forceNullable );
259
+ $ graphqlType = $ this ->convertType ($ type , $ input , $ rootOperation , $ resourceClass ?? '' , $ rootResource , $ property , $ depth , $ forceNullable );
276
260
277
261
$ graphqlWrappedType = $ graphqlType instanceof WrappingType ? $ graphqlType ->getWrappedType (true ) : $ graphqlType ;
278
262
$ isStandardGraphqlType = \in_array ($ graphqlWrappedType , GraphQLType::getStandardTypes (), true );
@@ -287,22 +271,43 @@ private function getResourceFieldConfiguration(?string $property, ?string $field
287
271
288
272
$ args = [];
289
273
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
+
290
285
if (!$ input && !$ rootOperation instanceof Mutation && !$ rootOperation instanceof Subscription && !$ isStandardGraphqlType && $ isCollectionType ) {
291
- if ($ this ->pagination ->isGraphQlEnabled ($ resourceOperation )) {
292
- $ args = $ this ->getGraphQlPaginationArgs ($ resourceOperation );
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
+ }
293
298
}
294
299
295
- $ args = $ this ->getFilterArgs ($ args , $ resourceClass , $ rootResource , $ resourceOperation , $ rootOperation , $ property , $ depth );
300
+ $ args = $ this ->getFilterArgs ($ args , $ resourceClass , $ rootResource , $ rootOperation , $ property , $ depth, $ operation );
296
301
}
297
302
298
303
if ($ isStandardGraphqlType || $ input ) {
299
304
$ resolve = null ;
300
305
} elseif (($ rootOperation instanceof Mutation || $ rootOperation instanceof Subscription) && $ depth <= 0 ) {
301
- $ resolve = $ rootOperation instanceof Mutation ? ($ this ->itemMutationResolverFactory )($ resourceClass , $ rootResource , $ resourceOperation ) : ($ this ->itemSubscriptionResolverFactory )($ resourceClass , $ rootResource , $ resourceOperation );
306
+ $ resolve = $ rootOperation instanceof Mutation ? ($ this ->itemMutationResolverFactory )($ resourceClass , $ rootResource , $ resolverOperation ) : ($ this ->itemSubscriptionResolverFactory )($ resourceClass , $ rootResource , $ resolverOperation );
302
307
} elseif ($ this ->typeBuilder ->isCollection ($ type )) {
303
- $ resolve = ($ this ->collectionResolverFactory )($ resourceClass , $ rootResource , $ resourceOperation );
308
+ $ resolve = ($ this ->collectionResolverFactory )($ resourceClass , $ rootResource , $ resolverOperation );
304
309
} else {
305
- $ resolve = ($ this ->itemResolverFactory )($ resourceClass , $ rootResource , $ resourceOperation );
310
+ $ resolve = ($ this ->itemResolverFactory )($ resourceClass , $ rootResource , $ resolverOperation );
306
311
}
307
312
308
313
return [
@@ -363,21 +368,21 @@ private function getGraphQlPaginationArgs(Operation $queryOperation): array
363
368
return $ args ;
364
369
}
365
370
366
- private function getFilterArgs (array $ args , ?string $ resourceClass , string $ rootResource , Operation $ resourceOperation , Operation $ rootOperation , ?string $ property , int $ depth ): array
371
+ private function getFilterArgs (array $ args , ?string $ resourceClass , string $ rootResource , Operation $ rootOperation , ?string $ property , int $ depth, ? AbstractOperation $ operation = null ): array
367
372
{
368
- if (null === $ resourceClass ) {
373
+ if (null === $ operation || null === $ resourceClass ) {
369
374
return $ args ;
370
375
}
371
376
372
- foreach ($ resourceOperation ->getFilters () ?? [] as $ filterId ) {
377
+ foreach ($ operation ->getFilters () ?? [] as $ filterId ) {
373
378
if (!$ this ->filterLocator ->has ($ filterId )) {
374
379
continue ;
375
380
}
376
381
377
382
foreach ($ this ->filterLocator ->get ($ filterId )->getDescription ($ resourceClass ) as $ key => $ value ) {
378
383
$ nullable = isset ($ value ['required ' ]) ? !$ value ['required ' ] : true ;
379
384
$ filterType = \in_array ($ value ['type ' ], Type::$ builtinTypes , true ) ? new Type ($ value ['type ' ], $ nullable ) : new Type ('object ' , $ nullable , $ value ['type ' ]);
380
- $ graphqlFilterType = $ this ->convertType ($ filterType , false , $ resourceOperation , $ rootOperation , $ resourceClass , $ rootResource , $ property , $ depth );
385
+ $ graphqlFilterType = $ this ->convertType ($ filterType , false , $ rootOperation , $ resourceClass , $ rootResource , $ property , $ depth );
381
386
382
387
if (str_ends_with ($ key , '[] ' )) {
383
388
$ graphqlFilterType = GraphQLType::listOf ($ graphqlFilterType );
@@ -394,14 +399,14 @@ private function getFilterArgs(array $args, ?string $resourceClass, string $root
394
399
array_walk_recursive ($ parsed , static function (&$ value ) use ($ graphqlFilterType ): void {
395
400
$ value = $ graphqlFilterType ;
396
401
});
397
- $ args = $ this ->mergeFilterArgs ($ args , $ parsed , $ resourceOperation , $ key );
402
+ $ args = $ this ->mergeFilterArgs ($ args , $ parsed , $ operation , $ key );
398
403
}
399
404
}
400
405
401
406
return $ this ->convertFilterArgsToTypes ($ args );
402
407
}
403
408
404
- private function mergeFilterArgs (array $ args , array $ parsed , ?Operation $ operation = null , string $ original = '' ): array
409
+ private function mergeFilterArgs (array $ args , array $ parsed , ?AbstractOperation $ operation = null , string $ original = '' ): array
405
410
{
406
411
foreach ($ parsed as $ key => $ value ) {
407
412
// Never override keys that cannot be merged
@@ -465,7 +470,7 @@ private function convertFilterArgsToTypes(array $args): array
465
470
*
466
471
* @throws InvalidTypeException
467
472
*/
468
- 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
473
+ private function convertType (Type $ type , bool $ input , Operation $ rootOperation , string $ resourceClass , string $ rootResource , ?string $ property , int $ depth , bool $ forceNullable = false ): GraphQLType |ListOfType |NonNull
469
474
{
470
475
$ graphqlType = $ this ->typeConverter ->convertType ($ type , $ input , $ rootOperation , $ resourceClass , $ rootResource , $ property , $ depth );
471
476
@@ -482,7 +487,7 @@ private function convertType(Type $type, bool $input, Operation $resourceOperati
482
487
}
483
488
484
489
if ($ this ->typeBuilder ->isCollection ($ type )) {
485
- return $ this ->pagination ->isGraphQlEnabled ($ resourceOperation ) && !$ input ? $ this ->typeBuilder ->getResourcePaginatedCollectionType ($ graphqlType , $ resourceOperation ) : GraphQLType::listOf ($ graphqlType );
490
+ return $ this ->pagination ->isGraphQlEnabled ($ rootOperation ) && !$ input ? $ this ->typeBuilder ->getResourcePaginatedCollectionType ($ graphqlType , $ resourceClass , $ rootOperation ) : GraphQLType::listOf ($ graphqlType );
486
491
}
487
492
488
493
return $ forceNullable || !$ graphqlType instanceof NullableType || $ type ->isNullable () || ($ rootOperation instanceof Mutation && 'update ' === $ rootOperation ->getName ())
0 commit comments