FieldsBuilder::resolveResourceArgs()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 3
nop 3
dl 0
loc 11
rs 10
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace ApiPlatform\Core\GraphQl\Type;
15
16
use ApiPlatform\Core\DataProvider\Pagination;
17
use ApiPlatform\Core\Exception\ResourceClassNotFoundException;
18
use ApiPlatform\Core\GraphQl\Resolver\Factory\ResolverFactoryInterface;
19
use ApiPlatform\Core\GraphQl\Type\Definition\TypeInterface;
20
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
21
use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
22
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
23
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
24
use Doctrine\Common\Inflector\Inflector;
25
use GraphQL\Type\Definition\InputObjectType;
26
use GraphQL\Type\Definition\NullableType;
27
use GraphQL\Type\Definition\Type as GraphQLType;
28
use GraphQL\Type\Definition\WrappingType;
29
use Psr\Container\ContainerInterface;
30
use Symfony\Component\Config\Definition\Exception\InvalidTypeException;
31
use Symfony\Component\PropertyInfo\Type;
32
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
33
34
/**
35
 * Builds the GraphQL fields.
36
 *
37
 * @experimental
38
 *
39
 * @author Alan Poulain <[email protected]>
40
 */
41
final class FieldsBuilder implements FieldsBuilderInterface
42
{
43
    private $propertyNameCollectionFactory;
44
    private $propertyMetadataFactory;
45
    private $resourceMetadataFactory;
46
    private $typesContainer;
47
    private $typeBuilder;
48
    private $typeConverter;
49
    private $itemResolverFactory;
50
    private $collectionResolverFactory;
51
    private $itemMutationResolverFactory;
52
    private $itemSubscriptionResolverFactory;
53
    private $filterLocator;
54
    private $pagination;
55
    private $nameConverter;
56
    private $nestingSeparator;
57
58
    public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ResourceMetadataFactoryInterface $resourceMetadataFactory, TypesContainerInterface $typesContainer, TypeBuilderInterface $typeBuilder, TypeConverterInterface $typeConverter, ResolverFactoryInterface $itemResolverFactory, ResolverFactoryInterface $collectionResolverFactory, ResolverFactoryInterface $itemMutationResolverFactory, ResolverFactoryInterface $itemSubscriptionResolverFactory, ContainerInterface $filterLocator, Pagination $pagination, ?NameConverterInterface $nameConverter, string $nestingSeparator)
59
    {
60
        $this->propertyNameCollectionFactory = $propertyNameCollectionFactory;
61
        $this->propertyMetadataFactory = $propertyMetadataFactory;
62
        $this->resourceMetadataFactory = $resourceMetadataFactory;
63
        $this->typesContainer = $typesContainer;
64
        $this->typeBuilder = $typeBuilder;
65
        $this->typeConverter = $typeConverter;
66
        $this->itemResolverFactory = $itemResolverFactory;
67
        $this->collectionResolverFactory = $collectionResolverFactory;
68
        $this->itemMutationResolverFactory = $itemMutationResolverFactory;
69
        $this->itemSubscriptionResolverFactory = $itemSubscriptionResolverFactory;
70
        $this->filterLocator = $filterLocator;
71
        $this->pagination = $pagination;
72
        $this->nameConverter = $nameConverter;
73
        $this->nestingSeparator = $nestingSeparator;
74
    }
75
76
    /**
77
     * {@inheritdoc}
78
     */
79
    public function getNodeQueryFields(): array
80
    {
81
        return [
82
            'type' => $this->typeBuilder->getNodeInterface(),
83
            'args' => [
84
                'id' => ['type' => GraphQLType::nonNull(GraphQLType::id())],
85
            ],
86
            'resolve' => ($this->itemResolverFactory)(),
87
        ];
88
    }
89
90
    /**
91
     * {@inheritdoc}
92
     */
93
    public function getItemQueryFields(string $resourceClass, ResourceMetadata $resourceMetadata, string $queryName, array $configuration): array
94
    {
95
        $shortName = $resourceMetadata->getShortName();
96
        $fieldName = lcfirst('item_query' === $queryName ? $shortName : $queryName.$shortName);
97
98
        $deprecationReason = (string) $resourceMetadata->getGraphqlAttribute($queryName, 'deprecation_reason', '', true);
99
100
        if ($fieldConfiguration = $this->getResourceFieldConfiguration(null, null, $deprecationReason, new Type(Type::BUILTIN_TYPE_OBJECT, true, $resourceClass), $resourceClass, false, $queryName, null, null)) {
101
            $args = $this->resolveResourceArgs($configuration['args'] ?? [], $queryName, $shortName);
0 ignored issues
show
Bug introduced by
It seems like $shortName can also be of type null; however, parameter $shortName of ApiPlatform\Core\GraphQl...::resolveResourceArgs() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

101
            $args = $this->resolveResourceArgs($configuration['args'] ?? [], $queryName, /** @scrutinizer ignore-type */ $shortName);
Loading history...
102
            $configuration['args'] = $args ?: $configuration['args'] ?? ['id' => ['type' => GraphQLType::nonNull(GraphQLType::id())]];
103
104
            return [$fieldName => array_merge($fieldConfiguration, $configuration)];
105
        }
106
107
        return [];
108
    }
109
110
    /**
111
     * {@inheritdoc}
112
     */
113
    public function getCollectionQueryFields(string $resourceClass, ResourceMetadata $resourceMetadata, string $queryName, array $configuration): array
114
    {
115
        $shortName = $resourceMetadata->getShortName();
116
        $fieldName = lcfirst('collection_query' === $queryName ? $shortName : $queryName.$shortName);
117
118
        $deprecationReason = (string) $resourceMetadata->getGraphqlAttribute($queryName, 'deprecation_reason', '', true);
119
120
        if ($fieldConfiguration = $this->getResourceFieldConfiguration(null, null, $deprecationReason, new Type(Type::BUILTIN_TYPE_OBJECT, false, null, true, null, new Type(Type::BUILTIN_TYPE_OBJECT, false, $resourceClass)), $resourceClass, false, $queryName, null, null)) {
121
            $args = $this->resolveResourceArgs($configuration['args'] ?? [], $queryName, $shortName);
0 ignored issues
show
Bug introduced by
It seems like $shortName can also be of type null; however, parameter $shortName of ApiPlatform\Core\GraphQl...::resolveResourceArgs() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

121
            $args = $this->resolveResourceArgs($configuration['args'] ?? [], $queryName, /** @scrutinizer ignore-type */ $shortName);
Loading history...
122
            $configuration['args'] = $args ?: $configuration['args'] ?? $fieldConfiguration['args'];
123
124
            return [Inflector::pluralize($fieldName) => array_merge($fieldConfiguration, $configuration)];
125
        }
126
127
        return [];
128
    }
129
130
    /**
131
     * {@inheritdoc}
132
     */
133
    public function getMutationFields(string $resourceClass, ResourceMetadata $resourceMetadata, string $mutationName): array
134
    {
135
        $mutationFields = [];
136
        $shortName = $resourceMetadata->getShortName();
137
        $resourceType = new Type(Type::BUILTIN_TYPE_OBJECT, true, $resourceClass);
138
        $deprecationReason = $resourceMetadata->getGraphqlAttribute($mutationName, 'deprecation_reason', '', true);
139
140
        if ($fieldConfiguration = $this->getResourceFieldConfiguration(null, ucfirst("{$mutationName}s a $shortName."), $deprecationReason, $resourceType, $resourceClass, false, null, $mutationName, null)) {
141
            $fieldConfiguration['args'] += ['input' => $this->getResourceFieldConfiguration(null, null, $deprecationReason, $resourceType, $resourceClass, true, null, $mutationName, null)];
142
        }
143
144
        $mutationFields[$mutationName.$resourceMetadata->getShortName()] = $fieldConfiguration ?? [];
145
146
        return $mutationFields;
147
    }
148
149
    /**
150
     * {@inheritdoc}
151
     */
152
    public function getSubscriptionFields(string $resourceClass, ResourceMetadata $resourceMetadata, string $subscriptionName): array
153
    {
154
        $subscriptionFields = [];
155
        $shortName = $resourceMetadata->getShortName();
156
        $resourceType = new Type(Type::BUILTIN_TYPE_OBJECT, true, $resourceClass);
157
        $deprecationReason = $resourceMetadata->getGraphqlAttribute($subscriptionName, 'deprecation_reason', '', true);
158
159
        if ($fieldConfiguration = $this->getResourceFieldConfiguration(null, "Subscribes to the $subscriptionName event of a $shortName.", $deprecationReason, $resourceType, $resourceClass, false, null, null, $subscriptionName)) {
160
            $fieldConfiguration['args'] += ['input' => $this->getResourceFieldConfiguration(null, null, $deprecationReason, $resourceType, $resourceClass, true, null, null, $subscriptionName)];
161
        }
162
163
        if (!$fieldConfiguration) {
164
            return [];
165
        }
166
167
        $subscriptionFields[$subscriptionName.$resourceMetadata->getShortName().'Subscribe'] = $fieldConfiguration;
168
169
        return $subscriptionFields;
170
    }
171
172
    /**
173
     * {@inheritdoc}
174
     */
175
    public function getResourceObjectTypeFields(?string $resourceClass, ResourceMetadata $resourceMetadata, bool $input, ?string $queryName, ?string $mutationName, ?string $subscriptionName, int $depth = 0, ?array $ioMetadata = null): array
176
    {
177
        $fields = [];
178
        $idField = ['type' => GraphQLType::nonNull(GraphQLType::id())];
179
        $clientMutationId = GraphQLType::string();
180
        $clientSubscriptionId = GraphQLType::string();
181
182
        if (null !== $ioMetadata && \array_key_exists('class', $ioMetadata) && null === $ioMetadata['class']) {
183
            if ($input) {
184
                return ['clientMutationId' => $clientMutationId];
185
            }
186
187
            return [];
188
        }
189
190
        if (null !== $subscriptionName && $input) {
191
            return [
192
                'id' => $idField,
193
                'clientSubscriptionId' => $clientSubscriptionId,
194
            ];
195
        }
196
197
        if ('delete' === $mutationName) {
198
            $fields = [
199
                'id' => $idField,
200
            ];
201
202
            if ($input) {
203
                $fields['clientMutationId'] = $clientMutationId;
204
            }
205
206
            return $fields;
207
        }
208
209
        if (!$input || 'create' !== $mutationName) {
210
            $fields['id'] = $idField;
211
        }
212
213
        ++$depth; // increment the depth for the call to getResourceFieldConfiguration.
214
215
        if (null !== $resourceClass) {
216
            foreach ($this->propertyNameCollectionFactory->create($resourceClass) as $property) {
217
                $propertyMetadata = $this->propertyMetadataFactory->create($resourceClass, $property, ['graphql_operation_name' => $subscriptionName ?? $mutationName ?? $queryName]);
218
                if (
219
                    null === ($propertyType = $propertyMetadata->getType())
220
                    || (!$input && false === $propertyMetadata->isReadable())
221
                    || ($input && null !== $mutationName && false === $propertyMetadata->isWritable())
222
                ) {
223
                    continue;
224
                }
225
226
                if ($fieldConfiguration = $this->getResourceFieldConfiguration($property, $propertyMetadata->getDescription(), $propertyMetadata->getAttribute('deprecation_reason', ''), $propertyType, $resourceClass, $input, $queryName, $mutationName, $subscriptionName, $depth)) {
227
                    $fields['id' === $property ? '_id' : $this->normalizePropertyName($property)] = $fieldConfiguration;
228
                }
229
            }
230
        }
231
232
        if (null !== $mutationName && $input) {
233
            $fields['clientMutationId'] = $clientMutationId;
234
        }
235
236
        return $fields;
237
    }
238
239
    /**
240
     * {@inheritdoc}
241
     */
242
    public function resolveResourceArgs(array $args, string $operationName, string $shortName): array
243
    {
244
        foreach ($args as $id => $arg) {
245
            if (!isset($arg['type'])) {
246
                throw new \InvalidArgumentException(sprintf('The argument "%s" of the custom operation "%s" in %s needs a "type" option.', $id, $operationName, $shortName));
247
            }
248
249
            $args[$id]['type'] = $this->typeConverter->resolveType($arg['type']);
250
        }
251
252
        return $args;
253
    }
254
255
    /**
256
     * Get the field configuration of a resource.
257
     *
258
     * @see http://webonyx.github.io/graphql-php/type-system/object-types/
259
     */
260
    private function getResourceFieldConfiguration(?string $property, ?string $fieldDescription, string $deprecationReason, Type $type, string $rootResource, bool $input, ?string $queryName, ?string $mutationName, ?string $subscriptionName, int $depth = 0): ?array
261
    {
262
        try {
263
            $resourceClass = $this->typeBuilder->isCollection($type) && ($collectionValueType = $type->getCollectionValueType()) ? $collectionValueType->getClassName() : $type->getClassName();
264
265
            if (null === $graphqlType = $this->convertType($type, $input, $queryName, $mutationName, $subscriptionName, $resourceClass ?? '', $rootResource, $property, $depth)) {
0 ignored issues
show
introduced by
The condition null === $graphqlType = ...rce, $property, $depth) is always false.
Loading history...
266
                return null;
267
            }
268
269
            $graphqlWrappedType = $graphqlType instanceof WrappingType ? $graphqlType->getWrappedType() : $graphqlType;
270
            $isStandardGraphqlType = \in_array($graphqlWrappedType, GraphQLType::getStandardTypes(), true);
271
            if ($isStandardGraphqlType) {
272
                $resourceClass = '';
273
            }
274
275
            $resourceMetadata = null;
276
            if (!empty($resourceClass)) {
277
                try {
278
                    $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
279
                } catch (ResourceClassNotFoundException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
280
                }
281
            }
282
283
            // Check mercure attribute if it's a subscription at the root level.
284
            if ($subscriptionName && null === $property && (!$resourceMetadata || !$resourceMetadata->getAttribute('mercure', false))) {
285
                return null;
286
            }
287
288
            $args = [];
289
            if (!$input && null === $mutationName && null === $subscriptionName && !$isStandardGraphqlType && $this->typeBuilder->isCollection($type)) {
290
                if ($this->pagination->isGraphQlEnabled($resourceClass, $queryName)) {
291
                    $args = $this->getGraphQlPaginationArgs($resourceClass, $queryName);
0 ignored issues
show
Bug introduced by
It seems like $resourceClass can also be of type null; however, parameter $resourceClass of ApiPlatform\Core\GraphQl...GraphQlPaginationArgs() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

291
                    $args = $this->getGraphQlPaginationArgs(/** @scrutinizer ignore-type */ $resourceClass, $queryName);
Loading history...
Bug introduced by
It seems like $queryName can also be of type null; however, parameter $queryName of ApiPlatform\Core\GraphQl...GraphQlPaginationArgs() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

291
                    $args = $this->getGraphQlPaginationArgs($resourceClass, /** @scrutinizer ignore-type */ $queryName);
Loading history...
292
                }
293
294
                $args = $this->getFilterArgs($args, $resourceClass, $resourceMetadata, $rootResource, $property, $queryName, $mutationName, $depth);
295
            }
296
297
            if ($isStandardGraphqlType || $input) {
298
                $resolve = null;
299
            } elseif ($mutationName) {
300
                $resolve = ($this->itemMutationResolverFactory)($resourceClass, $rootResource, $mutationName);
301
            } elseif ($subscriptionName) {
302
                $resolve = ($this->itemSubscriptionResolverFactory)($resourceClass, $rootResource, $subscriptionName);
303
            } elseif ($this->typeBuilder->isCollection($type)) {
304
                $resolve = ($this->collectionResolverFactory)($resourceClass, $rootResource, $queryName);
305
            } else {
306
                $resolve = ($this->itemResolverFactory)($resourceClass, $rootResource, $queryName);
307
            }
308
309
            return [
310
                'type' => $graphqlType,
311
                'description' => $fieldDescription,
312
                'args' => $args,
313
                'resolve' => $resolve,
314
                'deprecationReason' => $deprecationReason,
315
            ];
316
        } catch (InvalidTypeException $e) {
317
            // just ignore invalid types
318
        }
319
320
        return null;
321
    }
322
323
    private function getGraphQlPaginationArgs(string $resourceClass, string $queryName): array
324
    {
325
        $paginationType = $this->pagination->getGraphQlPaginationType($resourceClass, $queryName);
326
327
        if ('cursor' === $paginationType) {
328
            return [
329
                'first' => [
330
                    'type' => GraphQLType::int(),
331
                    'description' => 'Returns the first n elements from the list.',
332
                ],
333
                'last' => [
334
                    'type' => GraphQLType::int(),
335
                    'description' => 'Returns the last n elements from the list.',
336
                ],
337
                'before' => [
338
                    'type' => GraphQLType::string(),
339
                    'description' => 'Returns the elements in the list that come before the specified cursor.',
340
                ],
341
                'after' => [
342
                    'type' => GraphQLType::string(),
343
                    'description' => 'Returns the elements in the list that come after the specified cursor.',
344
                ],
345
            ];
346
        }
347
348
        $paginationOptions = $this->pagination->getOptions();
349
350
        $args = [
351
            $paginationOptions['page_parameter_name'] => [
352
                'type' => GraphQLType::int(),
353
                'description' => 'Returns the current page.',
354
            ],
355
        ];
356
357
        if ($paginationOptions['client_items_per_page']) {
358
            $args[$paginationOptions['items_per_page_parameter_name']] = [
359
                'type' => GraphQLType::int(),
360
                'description' => 'Returns the number of items per page.',
361
            ];
362
        }
363
364
        return $args;
365
    }
366
367
    private function getFilterArgs(array $args, ?string $resourceClass, ?ResourceMetadata $resourceMetadata, string $rootResource, ?string $property, ?string $queryName, ?string $mutationName, int $depth): array
368
    {
369
        if (null === $resourceMetadata || null === $resourceClass) {
370
            return $args;
371
        }
372
373
        foreach ($resourceMetadata->getGraphqlAttribute($queryName, 'filters', [], true) as $filterId) {
0 ignored issues
show
Bug introduced by
It seems like $queryName can also be of type null; however, parameter $operationName of ApiPlatform\Core\Metadat...::getGraphqlAttribute() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

373
        foreach ($resourceMetadata->getGraphqlAttribute(/** @scrutinizer ignore-type */ $queryName, 'filters', [], true) as $filterId) {
Loading history...
374
            if (null === $this->filterLocator || !$this->filterLocator->has($filterId)) {
375
                continue;
376
            }
377
378
            foreach ($this->filterLocator->get($filterId)->getDescription($resourceClass) as $key => $value) {
379
                $nullable = isset($value['required']) ? !$value['required'] : true;
380
                $filterType = \in_array($value['type'], Type::$builtinTypes, true) ? new Type($value['type'], $nullable) : new Type('object', $nullable, $value['type']);
381
                $graphqlFilterType = $this->convertType($filterType, false, $queryName, $mutationName, null, $resourceClass, $rootResource, $property, $depth);
382
383
                if ('[]' === substr($key, -2)) {
384
                    $graphqlFilterType = GraphQLType::listOf($graphqlFilterType);
385
                    $key = substr($key, 0, -2).'_list';
386
                }
387
388
                /** @var string $key */
389
                $key = str_replace('.', $this->nestingSeparator, $key);
390
391
                parse_str($key, $parsed);
392
                if (\array_key_exists($key, $parsed) && \is_array($parsed[$key])) {
393
                    $parsed = [$key => ''];
394
                }
395
                array_walk_recursive($parsed, function (&$value) use ($graphqlFilterType) {
396
                    $value = $graphqlFilterType;
397
                });
398
                $args = $this->mergeFilterArgs($args, $parsed, $resourceMetadata, $key);
399
            }
400
        }
401
402
        return $this->convertFilterArgsToTypes($args);
403
    }
404
405
    private function mergeFilterArgs(array $args, array $parsed, ResourceMetadata $resourceMetadata = null, $original = ''): array
406
    {
407
        foreach ($parsed as $key => $value) {
408
            // Never override keys that cannot be merged
409
            if (isset($args[$key]) && !\is_array($args[$key])) {
410
                continue;
411
            }
412
413
            if (\is_array($value)) {
414
                $value = $this->mergeFilterArgs($args[$key] ?? [], $value);
415
                if (!isset($value['#name'])) {
416
                    $name = (false === $pos = strrpos($original, '[')) ? $original : substr($original, 0, (int) $pos);
417
                    $value['#name'] = ($resourceMetadata ? $resourceMetadata->getShortName() : '').'Filter_'.strtr($name, ['[' => '_', ']' => '', '.' => '__']);
418
                }
419
            }
420
421
            $args[$key] = $value;
422
        }
423
424
        return $args;
425
    }
426
427
    private function convertFilterArgsToTypes(array $args): array
428
    {
429
        foreach ($args as $key => $value) {
430
            if (strpos($key, '.')) {
431
                // Declare relations/nested fields in a GraphQL compatible syntax.
432
                $args[str_replace('.', $this->nestingSeparator, $key)] = $value;
433
                unset($args[$key]);
434
            }
435
        }
436
437
        foreach ($args as $key => $value) {
438
            if (!\is_array($value) || !isset($value['#name'])) {
439
                continue;
440
            }
441
442
            $name = $value['#name'];
443
444
            if ($this->typesContainer->has($name)) {
445
                $args[$key] = $this->typesContainer->get($name);
446
                continue;
447
            }
448
449
            unset($value['#name']);
450
451
            $filterArgType = new InputObjectType([
452
                'name' => $name,
453
                'fields' => $this->convertFilterArgsToTypes($value),
454
            ]);
455
456
            $this->typesContainer->set($name, $filterArgType);
457
458
            $args[$key] = $filterArgType;
459
        }
460
461
        return $args;
462
    }
463
464
    /**
465
     * Converts a built-in type to its GraphQL equivalent.
466
     *
467
     * @throws InvalidTypeException
468
     */
469
    private function convertType(Type $type, bool $input, ?string $queryName, ?string $mutationName, ?string $subscriptionName, string $resourceClass, string $rootResource, ?string $property, int $depth)
470
    {
471
        $graphqlType = $this->typeConverter->convertType($type, $input, $queryName, $mutationName, $subscriptionName, $resourceClass, $rootResource, $property, $depth);
472
473
        if (null === $graphqlType) {
474
            throw new InvalidTypeException(sprintf('The type "%s" is not supported.', $type->getBuiltinType()));
475
        }
476
477
        if (\is_string($graphqlType)) {
478
            if (!$this->typesContainer->has($graphqlType)) {
479
                throw new InvalidTypeException(sprintf('The GraphQL type %s is not valid. Valid types are: %s. Have you registered this type by implementing %s?', $graphqlType, implode(', ', array_keys($this->typesContainer->all())), TypeInterface::class));
480
            }
481
482
            $graphqlType = $this->typesContainer->get($graphqlType);
483
        }
484
485
        if ($this->typeBuilder->isCollection($type)) {
486
            $operationName = $queryName ?? $mutationName ?? $subscriptionName;
487
488
            return $this->pagination->isGraphQlEnabled($resourceClass, $operationName) && !$input ? $this->typeBuilder->getResourcePaginatedCollectionType($graphqlType, $resourceClass, $operationName) : GraphQLType::listOf($graphqlType);
0 ignored issues
show
Bug introduced by
It seems like $operationName can also be of type null; however, parameter $operationName of ApiPlatform\Core\GraphQl...ginatedCollectionType() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

488
            return $this->pagination->isGraphQlEnabled($resourceClass, $operationName) && !$input ? $this->typeBuilder->getResourcePaginatedCollectionType($graphqlType, $resourceClass, /** @scrutinizer ignore-type */ $operationName) : GraphQLType::listOf($graphqlType);
Loading history...
489
        }
490
491
        return !$graphqlType instanceof NullableType || $type->isNullable() || (null !== $mutationName && 'update' === $mutationName)
492
            ? $graphqlType
493
            : GraphQLType::nonNull($graphqlType);
494
    }
495
496
    private function normalizePropertyName(string $property): string
497
    {
498
        return null !== $this->nameConverter ? $this->nameConverter->normalize($property) : $property;
499
    }
500
}
501