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); |
||
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); |
||
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)) { |
||
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
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); |
||
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) { |
||
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); |
||
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 |