Skip to content

Commit c3779ea

Browse files
committed
fix(graphql): Support nullable embedded relations in GraphQL types
1 parent 804da1b commit c3779ea

File tree

4 files changed

+41
-9
lines changed

4 files changed

+41
-9
lines changed

src/GraphQl/Tests/Type/TypeBuilderTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,30 @@ public function testGetResourceObjectTypeNestedInput(): void
217217
$wrappedType->config['fields']();
218218
}
219219

220+
public function testGetResourceObjectTypeNestedInputNullable(): void
221+
{
222+
$resourceMetadata = new ResourceMetadataCollection('resourceClass', []);
223+
$this->typesContainerProphecy->has('customShortNameNullableNestedInput')->shouldBeCalled()->willReturn(false);
224+
$this->typesContainerProphecy->set('customShortNameNullableNestedInput', Argument::type(InputObjectType::class))->shouldBeCalled();
225+
$this->typesContainerProphecy->has('Node')->shouldBeCalled()->willReturn(false);
226+
$this->typesContainerProphecy->set('Node', Argument::type(InterfaceType::class))->shouldBeCalled();
227+
228+
/** @var Operation $operation */
229+
$operation = (new Mutation())->withName('custom')->withShortName('shortNameNullable')->withDescription('description nullable');
230+
/** @var InputObjectType $resourceObjectType */
231+
$resourceObjectType = $this->typeBuilder->getResourceObjectType('resourceClass', $resourceMetadata, $operation, true, false, 1, false);
232+
233+
$this->assertInstanceOf(InputObjectType::class, $resourceObjectType);
234+
$this->assertSame('customShortNameNullableNestedInput', $resourceObjectType->name);
235+
$this->assertSame('description nullable', $resourceObjectType->description);
236+
$this->assertArrayHasKey('fields', $resourceObjectType->config);
237+
238+
$fieldsBuilderProphecy = $this->prophesize(FieldsBuilderEnumInterface::class);
239+
$fieldsBuilderProphecy->getResourceObjectTypeFields('resourceClass', $operation, true, 1, null)->shouldBeCalled();
240+
$this->fieldsBuilderLocatorProphecy->get('api_platform.graphql.fields_builder')->shouldBeCalled()->willReturn($fieldsBuilderProphecy->reveal());
241+
$resourceObjectType->config['fields']();
242+
}
243+
220244
public function testGetResourceObjectTypeCustomMutationInputArgs(): void
221245
{
222246
$resourceMetadata = new ResourceMetadataCollection('resourceClass', []);

src/GraphQl/Type/TypeBuilder.php

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public function __construct(private readonly TypesContainerInterface $typesConta
4848
/**
4949
* {@inheritdoc}
5050
*/
51-
public function getResourceObjectType(?string $resourceClass, ResourceMetadataCollection $resourceMetadataCollection, Operation $operation, bool $input, bool $wrapped = false, int $depth = 0): GraphQLType
51+
public function getResourceObjectType(?string $resourceClass, ResourceMetadataCollection $resourceMetadataCollection, Operation $operation, bool $input, bool $wrapped = false, int $depth = 0, bool $required = true): GraphQLType
5252
{
5353
$shortName = $operation->getShortName();
5454
$operationName = $operation->getName();
@@ -86,8 +86,8 @@ public function getResourceObjectType(?string $resourceClass, ResourceMetadataCo
8686

8787
if ($this->typesContainer->has($shortName)) {
8888
$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])));
89+
if (!($resourceObjectType instanceof ObjectType || $resourceObjectType instanceof NonNull || $resourceObjectType instanceof InputObjectType)) {
90+
throw new \LogicException(sprintf('Expected GraphQL type "%s" to be %s.', $shortName, implode('|', [ObjectType::class, NonNull::class, InputObjectType::class])));
9191
}
9292

9393
return $resourceObjectType;
@@ -156,7 +156,15 @@ public function getResourceObjectType(?string $resourceClass, ResourceMetadataCo
156156
'interfaces' => $wrapData ? [] : [$this->getNodeInterface()],
157157
];
158158

159-
$resourceObjectType = $input ? GraphQLType::nonNull(new InputObjectType($configuration)) : new ObjectType($configuration);
159+
if ($input) {
160+
$resourceObjectType = new InputObjectType($configuration);
161+
if ($required) {
162+
$resourceObjectType = GraphQLType::nonNull($resourceObjectType);
163+
}
164+
} else {
165+
$resourceObjectType = new ObjectType($configuration);
166+
}
167+
160168
$this->typesContainer->set($shortName, $resourceObjectType);
161169

162170
return $resourceObjectType;

src/GraphQl/Type/TypeBuilderEnumInterface.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616
use ApiPlatform\Metadata\GraphQl\Operation;
1717
use ApiPlatform\Metadata\Resource\ResourceMetadataCollection;
1818
use GraphQL\Type\Definition\InterfaceType;
19-
use GraphQL\Type\Definition\NonNull;
20-
use GraphQL\Type\Definition\ObjectType;
2119
use GraphQL\Type\Definition\Type as GraphQLType;
2220
use Symfony\Component\PropertyInfo\Type;
2321

@@ -31,9 +29,9 @@ interface TypeBuilderEnumInterface
3129
/**
3230
* Gets the object type of the given resource.
3331
*
34-
* @return ObjectType|NonNull the object type, possibly wrapped by NonNull
32+
* @return GraphQLType the object type, possibly wrapped by NonNull
3533
*/
36-
public function getResourceObjectType(?string $resourceClass, ResourceMetadataCollection $resourceMetadataCollection, Operation $operation, bool $input, bool $wrapped = false, int $depth = 0): GraphQLType;
34+
public function getResourceObjectType(?string $resourceClass, ResourceMetadataCollection $resourceMetadataCollection, Operation $operation, bool $input, bool $wrapped = false, int $depth = 0, bool $required = false): GraphQLType;
3735

3836
/**
3937
* Get the interface type of a node.

src/GraphQl/Type/TypeConverter.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,9 @@ private function getResourceType(Type $type, bool $input, Operation $rootOperati
182182
throw new OperationNotFoundException();
183183
}
184184

185-
return $this->typeBuilder->getResourceObjectType($resourceClass, $resourceMetadataCollection, $operation, $input, false, $depth);
185+
$required = $propertyMetadata?->isRequired() ?? true;
186+
187+
return $this->typeBuilder->getResourceObjectType($resourceClass, $resourceMetadataCollection, $operation, $input, false, $depth, $required);
186188
}
187189

188190
private function resolveAstTypeNode(TypeNode $astTypeNode, string $fromType): ?GraphQLType

0 commit comments

Comments
 (0)