Skip to content

Commit 5386488

Browse files
authored
feat(laravel): enable graphQl support (#6550)
1 parent 03357fb commit 5386488

File tree

18 files changed

+614
-87
lines changed

18 files changed

+614
-87
lines changed

src/GraphQl/Tests/Type/FieldsBuilderTest.php

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,6 @@ class FieldsBuilderTest extends TestCase
6363
private ObjectProphecy $typeBuilderProphecy;
6464
private ObjectProphecy $typeConverterProphecy;
6565
private ObjectProphecy $itemResolverFactoryProphecy;
66-
private ObjectProphecy $collectionResolverFactoryProphecy;
67-
private ObjectProphecy $itemMutationResolverFactoryProphecy;
68-
private ObjectProphecy $itemSubscriptionResolverFactoryProphecy;
6966
private ObjectProphecy $filterLocatorProphecy;
7067
private ObjectProphecy $resourceClassResolverProphecy;
7168
private FieldsBuilder $fieldsBuilder;
@@ -82,17 +79,14 @@ protected function setUp(): void
8279
$this->typeBuilderProphecy = $this->prophesize(ContextAwareTypeBuilderInterface::class);
8380
$this->typeConverterProphecy = $this->prophesize(TypeConverterInterface::class);
8481
$this->itemResolverFactoryProphecy = $this->prophesize(ResolverFactoryInterface::class);
85-
$this->collectionResolverFactoryProphecy = $this->prophesize(ResolverFactoryInterface::class);
86-
$this->itemMutationResolverFactoryProphecy = $this->prophesize(ResolverFactoryInterface::class);
87-
$this->itemSubscriptionResolverFactoryProphecy = $this->prophesize(ResolverFactoryInterface::class);
8882
$this->filterLocatorProphecy = $this->prophesize(ContainerInterface::class);
8983
$this->resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
9084
$this->fieldsBuilder = $this->buildFieldsBuilder();
9185
}
9286

9387
private function buildFieldsBuilder(?AdvancedNameConverterInterface $advancedNameConverter = null): FieldsBuilder
9488
{
95-
return new FieldsBuilder($this->propertyNameCollectionFactoryProphecy->reveal(), $this->propertyMetadataFactoryProphecy->reveal(), $this->resourceMetadataCollectionFactoryProphecy->reveal(), $this->resourceClassResolverProphecy->reveal(), $this->typesContainerProphecy->reveal(), $this->typeBuilderProphecy->reveal(), $this->typeConverterProphecy->reveal(), $this->itemResolverFactoryProphecy->reveal(), $this->collectionResolverFactoryProphecy->reveal(), $this->itemMutationResolverFactoryProphecy->reveal(), $this->itemSubscriptionResolverFactoryProphecy->reveal(), $this->filterLocatorProphecy->reveal(), new Pagination(), $advancedNameConverter ?? new CustomConverter(), '__');
89+
return new FieldsBuilder($this->propertyNameCollectionFactoryProphecy->reveal(), $this->propertyMetadataFactoryProphecy->reveal(), $this->resourceMetadataCollectionFactoryProphecy->reveal(), $this->resourceClassResolverProphecy->reveal(), $this->typesContainerProphecy->reveal(), $this->typeBuilderProphecy->reveal(), $this->typeConverterProphecy->reveal(), $this->itemResolverFactoryProphecy->reveal(), $this->filterLocatorProphecy->reveal(), new Pagination(), $advancedNameConverter ?? new CustomConverter(), '__');
9690
}
9791

9892
public function testGetNodeQueryFields(): void
@@ -126,7 +120,7 @@ public function testGetItemQueryFields(string $resourceClass, Operation $operati
126120
$this->typeConverterProphecy->convertType(Argument::type(Type::class), false, Argument::that(static fn (Operation $arg): bool => $arg->getName() === $operation->getName()), $resourceClass, $resourceClass, null, 0)->willReturn($graphqlType);
127121
$this->typeConverterProphecy->resolveType(Argument::type('string'))->willReturn(GraphQLType::string());
128122
$this->typeBuilderProphecy->isCollection(Argument::type(Type::class))->willReturn(false);
129-
$this->itemResolverFactoryProphecy->__invoke($resourceClass, $resourceClass, $operation)->willReturn($resolver);
123+
$this->itemResolverFactoryProphecy->__invoke($resourceClass, $resourceClass, $operation, Argument::any())->willReturn($resolver);
130124

131125
$queryFields = $this->fieldsBuilder->getItemQueryFields($resourceClass, $operation, $configuration);
132126

@@ -206,7 +200,7 @@ public function testGetCollectionQueryFields(string $resourceClass, Operation $o
206200
$this->typeConverterProphecy->resolveType(Argument::type('string'))->willReturn(GraphQLType::string());
207201
$this->typeBuilderProphecy->isCollection(Argument::type(Type::class))->willReturn(true);
208202
$this->typeBuilderProphecy->getPaginatedCollectionType($graphqlType, $operation)->willReturn($graphqlType);
209-
$this->collectionResolverFactoryProphecy->__invoke($resourceClass, $resourceClass, $operation)->willReturn($resolver);
203+
$this->itemResolverFactoryProphecy->__invoke($resourceClass, $resourceClass, $operation, Argument::any())->willReturn($resolver);
210204
$this->filterLocatorProphecy->has('my_filter')->willReturn(true);
211205
$filterProphecy = $this->prophesize(FilterInterface::class);
212206
$filterProphecy->getDescription($resourceClass)->willReturn([
@@ -356,7 +350,7 @@ public function testGetMutationFields(string $resourceClass, Operation $operatio
356350
$this->typeConverterProphecy->convertType(Argument::type(Type::class), false, Argument::that(static fn (Operation $arg): bool => $arg->getName() === $operation->getName()), $resourceClass, $resourceClass, null, 0)->willReturn($graphqlType);
357351
$this->typeConverterProphecy->convertType(Argument::type(Type::class), true, Argument::that(static fn (Operation $arg): bool => $arg->getName() === $operation->getName()), $resourceClass, $resourceClass, null, 0)->willReturn($inputGraphqlType);
358352
$this->typeBuilderProphecy->isCollection(Argument::type(Type::class))->willReturn(false);
359-
$this->itemMutationResolverFactoryProphecy->__invoke($resourceClass, $resourceClass, $operation)->willReturn($mutationResolver);
353+
$this->itemResolverFactoryProphecy->__invoke($resourceClass, $resourceClass, $operation, Argument::any())->willReturn($mutationResolver);
360354

361355
$mutationFields = $this->fieldsBuilder->getMutationFields($resourceClass, $operation);
362356

@@ -417,7 +411,7 @@ public function testGetSubscriptionFields(string $resourceClass, Operation $oper
417411
$this->typeConverterProphecy->convertType(Argument::type(Type::class), true, Argument::that(static fn (Operation $arg): bool => $arg->getName() === $operation->getName()), $resourceClass, $resourceClass, null, 0)->willReturn($inputGraphqlType);
418412
$this->typeBuilderProphecy->isCollection(Argument::type(Type::class))->willReturn(false);
419413
$this->resourceMetadataCollectionFactoryProphecy->create($resourceClass)->willReturn(new ResourceMetadataCollection($resourceClass, [(new ApiResource())->withGraphQlOperations([$operation->getName() => $operation])]));
420-
$this->itemSubscriptionResolverFactoryProphecy->__invoke($resourceClass, $resourceClass, $operation)->willReturn($subscriptionResolver);
414+
$this->itemResolverFactoryProphecy->__invoke($resourceClass, $resourceClass, $operation, Argument::any())->willReturn($subscriptionResolver);
421415

422416
$subscriptionFields = $this->fieldsBuilder->getSubscriptionFields($resourceClass, $operation);
423417

@@ -489,14 +483,14 @@ public function testGetResourceObjectTypeFields(string $resourceClass, Operation
489483

490484
if ('propertyObject' === $propertyName) {
491485
$this->typeConverterProphecy->convertType(Argument::type(Type::class), Argument::type('bool'), Argument::that(static fn (Operation $arg): bool => $arg->getName() === $operation->getName()), 'objectClass', $resourceClass, $propertyName, $depth + 1)->willReturn(new ObjectType(['name' => 'objectType', 'fields' => []]));
492-
$this->itemResolverFactoryProphecy->__invoke('objectClass', $resourceClass, $operation)->willReturn(static function (): void {
486+
$this->itemResolverFactoryProphecy->__invoke('objectClass', $resourceClass, $operation, Argument::any())->willReturn(static function (): void {
493487
});
494488
}
495489
if ('propertyNestedResource' === $propertyName) {
496490
$nestedResourceQueryOperation = new Query();
497491
$this->resourceMetadataCollectionFactoryProphecy->create('nestedResourceClass')->willReturn(new ResourceMetadataCollection('nestedResourceClass', [(new ApiResource())->withGraphQlOperations(['item_query' => $nestedResourceQueryOperation])]));
498492
$this->typeConverterProphecy->convertType(Argument::type(Type::class), Argument::type('bool'), Argument::that(static fn (Operation $arg): bool => $arg->getName() === $operation->getName()), 'nestedResourceClass', $resourceClass, $propertyName, $depth + 1)->willReturn(new ObjectType(['name' => 'objectType', 'fields' => []]));
499-
$this->itemResolverFactoryProphecy->__invoke('nestedResourceClass', $resourceClass, $nestedResourceQueryOperation)->willReturn(static function (): void {
493+
$this->itemResolverFactoryProphecy->__invoke('nestedResourceClass', $resourceClass, $nestedResourceQueryOperation, Argument::any())->willReturn(static function (): void {
500494
});
501495
}
502496
}

src/GraphQl/Type/FieldsBuilder.php

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
use ApiPlatform\Doctrine\Odm\State\Options as ODMOptions;
1717
use ApiPlatform\Doctrine\Orm\State\Options;
18-
use ApiPlatform\GraphQl\Resolver\Factory\ResolverFactory;
1918
use ApiPlatform\GraphQl\Resolver\Factory\ResolverFactoryInterface;
2019
use ApiPlatform\GraphQl\Type\Definition\TypeInterface;
2120
use ApiPlatform\Metadata\GraphQl\Mutation;
@@ -51,7 +50,7 @@ final class FieldsBuilder implements FieldsBuilderEnumInterface
5150
{
5251
private readonly ContextAwareTypeBuilderInterface $typeBuilder;
5352

54-
public function __construct(private readonly PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, private readonly PropertyMetadataFactoryInterface $propertyMetadataFactory, private readonly ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory, private readonly ResourceClassResolverInterface $resourceClassResolver, private readonly TypesContainerInterface $typesContainer, ContextAwareTypeBuilderInterface $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, private readonly ?InflectorInterface $inflector = new Inflector())
53+
public function __construct(private readonly PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, private readonly PropertyMetadataFactoryInterface $propertyMetadataFactory, private readonly ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory, private readonly ResourceClassResolverInterface $resourceClassResolver, private readonly TypesContainerInterface $typesContainer, ContextAwareTypeBuilderInterface $typeBuilder, private readonly TypeConverterInterface $typeConverter, private readonly ResolverFactoryInterface $resolverFactory, private readonly ContainerInterface $filterLocator, private readonly Pagination $pagination, private readonly ?NameConverterInterface $nameConverter, private readonly string $nestingSeparator, private readonly ?InflectorInterface $inflector = new Inflector())
5554
{
5655
$this->typeBuilder = $typeBuilder;
5756
}
@@ -66,7 +65,7 @@ public function getNodeQueryFields(): array
6665
'args' => [
6766
'id' => ['type' => GraphQLType::nonNull(GraphQLType::id())],
6867
],
69-
'resolve' => ($this->itemResolverFactory)(),
68+
'resolve' => ($this->resolverFactory)(),
7069
];
7170
}
7271

@@ -450,22 +449,10 @@ private function getResourceFieldConfiguration(?string $property, ?string $field
450449
$args = $this->getFilterArgs($args, $resourceClass, $rootResource, $resourceOperation, $rootOperation, $property, $depth);
451450
}
452451

453-
if ($this->itemResolverFactory instanceof ResolverFactory) {
454-
if ($isStandardGraphqlType || $input) {
455-
$resolve = null;
456-
} else {
457-
$resolve = ($this->itemResolverFactory)($resourceClass, $rootResource, $resourceOperation, $this->propertyMetadataFactory);
458-
}
452+
if ($isStandardGraphqlType || $input) {
453+
$resolve = null;
459454
} else {
460-
if ($isStandardGraphqlType || $input) {
461-
$resolve = null;
462-
} elseif (($rootOperation instanceof Mutation || $rootOperation instanceof Subscription) && $depth <= 0) {
463-
$resolve = $rootOperation instanceof Mutation ? ($this->itemMutationResolverFactory)($resourceClass, $rootResource, $resourceOperation) : ($this->itemSubscriptionResolverFactory)($resourceClass, $rootResource, $resourceOperation);
464-
} elseif ($this->typeBuilder->isCollection($type)) {
465-
$resolve = ($this->collectionResolverFactory)($resourceClass, $rootResource, $resourceOperation);
466-
} else {
467-
$resolve = ($this->itemResolverFactory)($resourceClass, $rootResource, $resourceOperation);
468-
}
455+
$resolve = ($this->resolverFactory)($resourceClass, $rootResource, $resourceOperation, $this->propertyMetadataFactory);
469456
}
470457

471458
return [

src/GraphQl/Type/TypeBuilder.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,17 @@ final class TypeBuilder implements ContextAwareTypeBuilderInterface
4141
{
4242
private $defaultFieldResolver;
4343

44-
public function __construct(private readonly TypesContainerInterface $typesContainer, callable $defaultFieldResolver, private readonly ContainerInterface $fieldsBuilderLocator, private readonly Pagination $pagination)
44+
public function __construct(private readonly TypesContainerInterface $typesContainer, callable $defaultFieldResolver, private ?ContainerInterface $fieldsBuilderLocator, private readonly Pagination $pagination)
4545
{
46+
$this->fieldsBuilderLocator = $fieldsBuilderLocator;
4647
$this->defaultFieldResolver = $defaultFieldResolver;
4748
}
4849

50+
public function setFieldsBuilderLocator(ContainerInterface $fieldsBuilderLocator): void
51+
{
52+
$this->fieldsBuilderLocator = $fieldsBuilderLocator;
53+
}
54+
4955
/**
5056
* {@inheritdoc}
5157
*/

src/GraphQl/composer.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
"api-platform/metadata": "^3.2 || ^4.0",
2525
"api-platform/serializer": "^3.2 || ^4.0",
2626
"api-platform/state": "^3.2 || ^4.0",
27-
"api-platform/validator": "^3.2 || ^4.0",
2827
"symfony/property-info": "^6.4 || ^7.1",
2928
"symfony/serializer": "^6.4 || ^7.1",
3029
"webonyx/graphql-php": "^14.0 || ^15.0",
@@ -42,6 +41,9 @@
4241
"api-platform/doctrine-odm": "^3.2 || ^4.0",
4342
"api-platform/doctrine-orm": "^3.2 || ^4.0"
4443
},
44+
"suggest": {
45+
"api-platform/validator": "To support validation."
46+
},
4547
"autoload": {
4648
"psr-4": {
4749
"ApiPlatform\\GraphQl\\": ""

0 commit comments

Comments
 (0)