|
14 | 14 | namespace ApiPlatform\GraphQl\Type;
|
15 | 15 |
|
16 | 16 | use ApiPlatform\GraphQl\Serializer\ItemNormalizer;
|
| 17 | +use ApiPlatform\Metadata\ApiProperty; |
17 | 18 | use ApiPlatform\Metadata\CollectionOperationInterface;
|
18 | 19 | use ApiPlatform\Metadata\Exception\OperationNotFoundException;
|
19 | 20 | use ApiPlatform\Metadata\GraphQl\Mutation;
|
@@ -48,7 +49,7 @@ public function __construct(private readonly TypesContainerInterface $typesConta
|
48 | 49 | /**
|
49 | 50 | * {@inheritdoc}
|
50 | 51 | */
|
51 |
| - public function getResourceObjectType(?string $resourceClass, ResourceMetadataCollection $resourceMetadataCollection, Operation $operation, bool $input, bool $wrapped = false, int $depth = 0): GraphQLType |
| 52 | + public function getResourceObjectType(?string $resourceClass, ResourceMetadataCollection $resourceMetadataCollection, Operation $operation, bool $input, bool $wrapped = false, int $depth = 0, ApiProperty $propertyMetadata = null): GraphQLType |
52 | 53 | {
|
53 | 54 | $shortName = $operation->getShortName();
|
54 | 55 | $operationName = $operation->getName();
|
@@ -84,80 +85,84 @@ public function getResourceObjectType(?string $resourceClass, ResourceMetadataCo
|
84 | 85 | $shortName .= 'Data';
|
85 | 86 | }
|
86 | 87 |
|
87 |
| - if ($this->typesContainer->has($shortName)) { |
88 |
| - $resourceObjectType = $this->typesContainer->get($shortName); |
89 |
| - if (!($resourceObjectType instanceof ObjectType || $resourceObjectType instanceof NonNull)) { |
90 |
| - throw new \LogicException(sprintf('Expected GraphQL type "%s" to be %s.', $shortName, implode('|', [ObjectType::class, NonNull::class]))); |
| 88 | + $resourceObjectType = null; |
| 89 | + if (!$this->typesContainer->has($shortName)) { |
| 90 | + $ioMetadata = $input ? $operation->getInput() : $operation->getOutput(); |
| 91 | + if (null !== $ioMetadata && \array_key_exists('class', $ioMetadata) && null !== $ioMetadata['class']) { |
| 92 | + $resourceClass = $ioMetadata['class']; |
91 | 93 | }
|
92 | 94 |
|
93 |
| - return $resourceObjectType; |
94 |
| - } |
| 95 | + $wrapData = !$wrapped && ($operation instanceof Mutation || $operation instanceof Subscription) && !$input && $depth < 1; |
95 | 96 |
|
96 |
| - $ioMetadata = $input ? $operation->getInput() : $operation->getOutput(); |
97 |
| - if (null !== $ioMetadata && \array_key_exists('class', $ioMetadata) && null !== $ioMetadata['class']) { |
98 |
| - $resourceClass = $ioMetadata['class']; |
99 |
| - } |
| 97 | + $configuration = [ |
| 98 | + 'name' => $shortName, |
| 99 | + 'description' => $operation->getDescription(), |
| 100 | + 'resolveField' => $this->defaultFieldResolver, |
| 101 | + 'fields' => function () use ($resourceClass, $operation, $operationName, $resourceMetadataCollection, $input, $wrapData, $depth, $ioMetadata) { |
| 102 | + if ($wrapData) { |
| 103 | + $queryNormalizationContext = $this->getQueryOperation($resourceMetadataCollection)?->getNormalizationContext() ?? []; |
100 | 104 |
|
101 |
| - $wrapData = !$wrapped && ($operation instanceof Mutation || $operation instanceof Subscription) && !$input && $depth < 1; |
| 105 | + try { |
| 106 | + $mutationNormalizationContext = $operation instanceof Mutation || $operation instanceof Subscription ? ($resourceMetadataCollection->getOperation($operationName)->getNormalizationContext() ?? []) : []; |
| 107 | + } catch (OperationNotFoundException) { |
| 108 | + $mutationNormalizationContext = []; |
| 109 | + } |
| 110 | + // Use a new type for the wrapped object only if there is a specific normalization context for the mutation or the subscription. |
| 111 | + // If not, use the query type in order to ensure the client cache could be used. |
| 112 | + $useWrappedType = $queryNormalizationContext !== $mutationNormalizationContext; |
102 | 113 |
|
103 |
| - $configuration = [ |
104 |
| - 'name' => $shortName, |
105 |
| - 'description' => $operation->getDescription(), |
106 |
| - 'resolveField' => $this->defaultFieldResolver, |
107 |
| - 'fields' => function () use ($resourceClass, $operation, $operationName, $resourceMetadataCollection, $input, $wrapData, $depth, $ioMetadata) { |
108 |
| - if ($wrapData) { |
109 |
| - $queryNormalizationContext = $this->getQueryOperation($resourceMetadataCollection)?->getNormalizationContext() ?? []; |
110 |
| - |
111 |
| - try { |
112 |
| - $mutationNormalizationContext = $operation instanceof Mutation || $operation instanceof Subscription ? ($resourceMetadataCollection->getOperation($operationName)->getNormalizationContext() ?? []) : []; |
113 |
| - } catch (OperationNotFoundException) { |
114 |
| - $mutationNormalizationContext = []; |
115 |
| - } |
116 |
| - // Use a new type for the wrapped object only if there is a specific normalization context for the mutation or the subscription. |
117 |
| - // If not, use the query type in order to ensure the client cache could be used. |
118 |
| - $useWrappedType = $queryNormalizationContext !== $mutationNormalizationContext; |
| 114 | + $wrappedOperationName = $operationName; |
119 | 115 |
|
120 |
| - $wrappedOperationName = $operationName; |
| 116 | + if (!$useWrappedType) { |
| 117 | + $wrappedOperationName = $operation instanceof Query ? $operationName : 'item_query'; |
| 118 | + } |
121 | 119 |
|
122 |
| - if (!$useWrappedType) { |
123 |
| - $wrappedOperationName = $operation instanceof Query ? $operationName : 'item_query'; |
124 |
| - } |
| 120 | + $wrappedOperation = $resourceMetadataCollection->getOperation($wrappedOperationName); |
125 | 121 |
|
126 |
| - $wrappedOperation = $resourceMetadataCollection->getOperation($wrappedOperationName); |
| 122 | + $fields = [ |
| 123 | + lcfirst($wrappedOperation->getShortName()) => $this->getResourceObjectType($resourceClass, $resourceMetadataCollection, $wrappedOperation instanceof Operation ? $wrappedOperation : null, $input, true, $depth), |
| 124 | + ]; |
127 | 125 |
|
128 |
| - $fields = [ |
129 |
| - lcfirst($wrappedOperation->getShortName()) => $this->getResourceObjectType($resourceClass, $resourceMetadataCollection, $wrappedOperation instanceof Operation ? $wrappedOperation : null, $input, true, $depth), |
130 |
| - ]; |
| 126 | + if ($operation instanceof Subscription) { |
| 127 | + $fields['clientSubscriptionId'] = GraphQLType::string(); |
| 128 | + if ($operation->getMercure()) { |
| 129 | + $fields['mercureUrl'] = GraphQLType::string(); |
| 130 | + } |
131 | 131 |
|
132 |
| - if ($operation instanceof Subscription) { |
133 |
| - $fields['clientSubscriptionId'] = GraphQLType::string(); |
134 |
| - if ($operation->getMercure()) { |
135 |
| - $fields['mercureUrl'] = GraphQLType::string(); |
| 132 | + return $fields; |
136 | 133 | }
|
137 | 134 |
|
138 |
| - return $fields; |
| 135 | + return $fields + ['clientMutationId' => GraphQLType::string()]; |
139 | 136 | }
|
140 | 137 |
|
141 |
| - return $fields + ['clientMutationId' => GraphQLType::string()]; |
142 |
| - } |
| 138 | + $fieldsBuilder = $this->fieldsBuilderLocator->get('api_platform.graphql.fields_builder'); |
| 139 | + $fields = $fieldsBuilder->getResourceObjectTypeFields($resourceClass, $operation, $input, $depth, $ioMetadata); |
| 140 | + |
| 141 | + if ($input && $operation instanceof Mutation && null !== $mutationArgs = $operation->getArgs()) { |
| 142 | + return $fieldsBuilder->resolveResourceArgs($mutationArgs, $operation) + ['clientMutationId' => $fields['clientMutationId']]; |
| 143 | + } |
| 144 | + if ($input && $operation instanceof Mutation && null !== $extraMutationArgs = $operation->getExtraArgs()) { |
| 145 | + return $fields + $fieldsBuilder->resolveResourceArgs($extraMutationArgs, $operation); |
| 146 | + } |
143 | 147 |
|
144 |
| - $fieldsBuilder = $this->fieldsBuilderLocator->get('api_platform.graphql.fields_builder'); |
145 |
| - $fields = $fieldsBuilder->getResourceObjectTypeFields($resourceClass, $operation, $input, $depth, $ioMetadata); |
| 148 | + return $fields; |
| 149 | + }, |
| 150 | + 'interfaces' => $wrapData ? [] : [$this->getNodeInterface()], |
| 151 | + ]; |
146 | 152 |
|
147 |
| - if ($input && $operation instanceof Mutation && null !== $mutationArgs = $operation->getArgs()) { |
148 |
| - return $fieldsBuilder->resolveResourceArgs($mutationArgs, $operation) + ['clientMutationId' => $fields['clientMutationId']]; |
149 |
| - } |
150 |
| - if ($input && $operation instanceof Mutation && null !== $extraMutationArgs = $operation->getExtraArgs()) { |
151 |
| - return $fields + $fieldsBuilder->resolveResourceArgs($extraMutationArgs, $operation); |
152 |
| - } |
| 153 | + $resourceObjectType = $input ? new InputObjectType($configuration) : new ObjectType($configuration); |
| 154 | + $this->typesContainer->set($shortName, $resourceObjectType); |
| 155 | + } |
153 | 156 |
|
154 |
| - return $fields; |
155 |
| - }, |
156 |
| - 'interfaces' => $wrapData ? [] : [$this->getNodeInterface()], |
157 |
| - ]; |
| 157 | + $resourceObjectType = $resourceObjectType ?? $this->typesContainer->get($shortName); |
| 158 | + if (!($resourceObjectType instanceof ObjectType || $resourceObjectType instanceof NonNull || $resourceObjectType instanceof InputObjectType)) { |
| 159 | + throw new \LogicException(sprintf('Expected GraphQL type "%s" to be %s.', $shortName, implode('|', [ObjectType::class, NonNull::class, InputObjectType::class]))); |
| 160 | + } |
158 | 161 |
|
159 |
| - $resourceObjectType = $input ? GraphQLType::nonNull(new InputObjectType($configuration)) : new ObjectType($configuration); |
160 |
| - $this->typesContainer->set($shortName, $resourceObjectType); |
| 162 | + $required = $propertyMetadata?->isRequired() ?? true; |
| 163 | + if ($required && $input) { |
| 164 | + $resourceObjectType = GraphQLType::nonNull($resourceObjectType); |
| 165 | + } |
161 | 166 |
|
162 | 167 | return $resourceObjectType;
|
163 | 168 | }
|
|
0 commit comments