From bc96751e040406de65a690f53003c3db5626a013 Mon Sep 17 00:00:00 2001 From: jotwea Date: Fri, 15 Mar 2024 11:45:56 +0100 Subject: [PATCH] fix(graphql): nested collection for mongo (#6174) * fix(graphql): nested collection for mongo * introduce NoopProvider --------- Co-authored-by: josef.wagner --- features/graphql/query.feature | 2 +- .../Resolver/Factory/ResolverFactory.php | 3 +- src/GraphQl/Serializer/ItemNormalizer.php | 3 +- src/GraphQl/State/Provider/NoopProvider.php | 28 +++++++++++++++++++ .../Document/MultiRelationsDummy.php | 4 +-- .../Document/MultiRelationsNested.php | 3 +- .../MultiRelationsNestedPaginated.php | 3 +- tests/Fixtures/TestBundle/Document/SoMany.php | 3 ++ 8 files changed, 42 insertions(+), 7 deletions(-) create mode 100644 src/GraphQl/State/Provider/NoopProvider.php diff --git a/features/graphql/query.feature b/features/graphql/query.feature index 5ea86700e62..2021d9039a7 100644 --- a/features/graphql/query.feature +++ b/features/graphql/query.feature @@ -68,7 +68,7 @@ Feature: GraphQL query support And the JSON node "data.multiRelationsDummy.oneToManyRelations.edges[0].node.name" should match "#RelatedOneToManyDummy(1|3)2#" And the JSON node "data.multiRelationsDummy.oneToManyRelations.edges[2].node.name" should match "#RelatedOneToManyDummy(1|3)2#" - @createSchema @!mongodb + @createSchema Scenario: Retrieve embedded collections Given there are 2 multiRelationsDummy objects having each a manyToOneRelation, 2 manyToManyRelations, 3 oneToManyRelations and 4 embeddedRelations When I send the following GraphQL request: diff --git a/src/GraphQl/Resolver/Factory/ResolverFactory.php b/src/GraphQl/Resolver/Factory/ResolverFactory.php index b75055c9fe7..64faed799dc 100644 --- a/src/GraphQl/Resolver/Factory/ResolverFactory.php +++ b/src/GraphQl/Resolver/Factory/ResolverFactory.php @@ -13,6 +13,7 @@ namespace ApiPlatform\GraphQl\Resolver\Factory; +use ApiPlatform\GraphQl\State\Provider\NoopProvider; use ApiPlatform\Metadata\GraphQl\Mutation; use ApiPlatform\Metadata\GraphQl\Operation; use ApiPlatform\Metadata\GraphQl\Query; @@ -35,7 +36,7 @@ public function __invoke(?string $resourceClass = null, ?string $rootClass = nul // Data already fetched and normalized (field or nested resource) if ($body = $source[$info->fieldName] ?? null) { // special treatment for nested resources without a resolver/provider - if ($operation instanceof Query && $operation->getNested() && !$operation->getResolver() && !$operation->getProvider()) { + if ($operation instanceof Query && $operation->getNested() && !$operation->getResolver() && (!$operation->getProvider() || NoopProvider::class === $operation->getProvider())) { return $this->resolve($source, $args, $info, $rootClass, $operation, new ArrayPaginator($body, 0, \count($body))); } diff --git a/src/GraphQl/Serializer/ItemNormalizer.php b/src/GraphQl/Serializer/ItemNormalizer.php index 9f880de9141..d0c7c384079 100644 --- a/src/GraphQl/Serializer/ItemNormalizer.php +++ b/src/GraphQl/Serializer/ItemNormalizer.php @@ -13,6 +13,7 @@ namespace ApiPlatform\GraphQl\Serializer; +use ApiPlatform\GraphQl\State\Provider\NoopProvider; use ApiPlatform\Metadata\ApiProperty; use ApiPlatform\Metadata\GraphQl\Query; use ApiPlatform\Metadata\IdentifiersExtractorInterface; @@ -113,7 +114,7 @@ protected function normalizeCollectionOfRelations(ApiProperty $propertyMetadata, { // check for nested collection $operation = $this->resourceMetadataCollectionFactory?->create($resourceClass)->getOperation(forceCollection: true, forceGraphQl: true); - if ($operation instanceof Query && $operation->getNested() && !$operation->getResolver() && !$operation->getProvider()) { + if ($operation instanceof Query && $operation->getNested() && !$operation->getResolver() && (!$operation->getProvider() || NoopProvider::class === $operation->getProvider())) { return [...$attributeValue]; } diff --git a/src/GraphQl/State/Provider/NoopProvider.php b/src/GraphQl/State/Provider/NoopProvider.php new file mode 100644 index 00000000000..dc9abdd6e56 --- /dev/null +++ b/src/GraphQl/State/Provider/NoopProvider.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\GraphQl\State\Provider; + +use ApiPlatform\Metadata\Operation; +use ApiPlatform\State\ProviderInterface; + +/** + * No-op Provider for GraphQl. + */ +final class NoopProvider implements ProviderInterface +{ + public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null + { + return null; + } +} diff --git a/tests/Fixtures/TestBundle/Document/MultiRelationsDummy.php b/tests/Fixtures/TestBundle/Document/MultiRelationsDummy.php index 557918e0ee8..19733044e50 100644 --- a/tests/Fixtures/TestBundle/Document/MultiRelationsDummy.php +++ b/tests/Fixtures/TestBundle/Document/MultiRelationsDummy.php @@ -89,7 +89,7 @@ public function addOneToManyRelation(MultiRelationsRelatedDummy $relatedMultiUse public function getNestedCollection(): Collection { - return $this->nestedCollection; + return $this->nestedCollection->map(fn ($entry) => ['name' => $entry->name]); } public function setNestedCollection(Collection $nestedCollection): self @@ -101,7 +101,7 @@ public function setNestedCollection(Collection $nestedCollection): self public function getNestedPaginatedCollection(): Collection { - return $this->nestedPaginatedCollection; + return $this->nestedPaginatedCollection->map(fn ($entry) => ['name' => $entry->name]); } public function setNestedPaginatedCollection(Collection $nestedPaginatedCollection): self diff --git a/tests/Fixtures/TestBundle/Document/MultiRelationsNested.php b/tests/Fixtures/TestBundle/Document/MultiRelationsNested.php index 0dd6a36f2ee..8a2308f93d2 100644 --- a/tests/Fixtures/TestBundle/Document/MultiRelationsNested.php +++ b/tests/Fixtures/TestBundle/Document/MultiRelationsNested.php @@ -13,11 +13,12 @@ namespace ApiPlatform\Tests\Fixtures\TestBundle\Document; +use ApiPlatform\GraphQl\State\Provider\NoopProvider; use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\GraphQl\QueryCollection; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; -#[ApiResource(graphQlOperations: [new QueryCollection(paginationEnabled: false, nested: true)])] +#[ApiResource(graphQlOperations: [new QueryCollection(paginationEnabled: false, provider: NoopProvider::class, nested: true)])] #[ODM\EmbeddedDocument] class MultiRelationsNested { diff --git a/tests/Fixtures/TestBundle/Document/MultiRelationsNestedPaginated.php b/tests/Fixtures/TestBundle/Document/MultiRelationsNestedPaginated.php index f9327eddd02..11d83656b0b 100644 --- a/tests/Fixtures/TestBundle/Document/MultiRelationsNestedPaginated.php +++ b/tests/Fixtures/TestBundle/Document/MultiRelationsNestedPaginated.php @@ -13,11 +13,12 @@ namespace ApiPlatform\Tests\Fixtures\TestBundle\Document; +use ApiPlatform\GraphQl\State\Provider\NoopProvider; use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\GraphQl\QueryCollection; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; -#[ApiResource(graphQlOperations: [new QueryCollection(nested: true)])] +#[ApiResource(graphQlOperations: [new QueryCollection(provider: NoopProvider::class, nested: true)])] #[ODM\EmbeddedDocument] class MultiRelationsNestedPaginated { diff --git a/tests/Fixtures/TestBundle/Document/SoMany.php b/tests/Fixtures/TestBundle/Document/SoMany.php index 3fcc1fb03ef..62b6d5f4468 100644 --- a/tests/Fixtures/TestBundle/Document/SoMany.php +++ b/tests/Fixtures/TestBundle/Document/SoMany.php @@ -29,4 +29,7 @@ class SoMany public $id; #[ODM\Field(nullable: true)] public $content; + + #[ODM\ReferenceOne(targetDocument: FooDummy::class, storeAs: 'id', nullable: true)] + public ?FooDummy $fooDummy; }