fix(graphql): nested resources without graphqloperations propagate fields#8236
Merged
soyuka merged 2 commits intoJun 4, 2026
Merged
Conversation
…elds When a nested resource declares an empty `graphQlOperations` array, the framework auto-adds a `nested: true` Query/QueryCollection without a provider. Two issues prevented the parent provider's data from being returned through such relations: - `ResolverFactory` wrapped the body in `ArrayPaginator` for both Query (item) and QueryCollection operations, which broke single-item nested resources whose body is already a normalized associative array. - `SerializerContextBuilder` only unwrapped the `edges/node`/`collection` pagination layer at the root attribute selection, so the Symfony serializer recursed into nested relations with an attribute tree whose leaves were still wrapped, normalizing each item to an empty object. The wrap is now restricted to `CollectionOperationInterface` and the pagination unwrap is applied recursively in `replaceIdKeys`, letting the Symfony serializer descend with a tree that mirrors the actual resource shape. Fixes api-platform#8076
05cda95 to
b42fbb5
Compare
Address review feedback on PR api-platform#8236. - Drop the dedicated ProductProvider service. Inline the fixture provider as a static method on Product and reference it via `provider: [self::class, 'provide']`. The matching service tag in tests/Fixtures/app/config/config_common.yml is removed too. - Tighten the return type: the provider only ever returns a Product or an array of Product, so the `|null` union (flagged by PHPStan as unreachable on the previous ProductProvider::provide()) is gone. - Keep `is_array($body)` in ResolverFactory rather than swapping for `is_iterable`. The body fed into this branch comes from GraphQl ItemNormalizer::normalizeCollectionOfRelations, which spreads its input into a real array ([...$attributeValue]); no Traversable reaches this point. Switching to is_iterable would require an extra iterator_to_array conversion to keep count($body) safe, with no observed call site producing the wider type.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When a nested resource declares an empty
graphQlOperationsarray, the framework auto-addsnested: trueQuery/QueryCollection operations without a provider. The data already loaded by the parent provider was lost in two places:ResolverFactorywrapped item bodies inArrayPaginatorindiscriminately, andSerializerContextBuilderonly unwrapped theedges/node/collectionpagination layer at the root, so the Symfony serializer descended into nested relations with a still-wrapped attribute tree and normalized each item to an empty object.Reproduction
Querying
product { facility { variants { edges { node { sku }}}}}against a customProductProvider(whereFacilityandVariantdeclaregraphQlOperations: []) raisedProvider not found on operation "collection_query", and once that branch was fixed, fields likeskuwere silently dropped.Test plan
tests/Functional/GraphQl/Issue8076Test.phpcovering the bug.tests/Functional/GraphQlandsrc/GraphQl/Testssuites still pass locally.tests/Functionalsuite still passes.Fixes #8076