api-platform
api-platform copied to clipboard
Subresource with GraphQL (without Doctrine support) only yields results for oneToMany relationships, returns null for oneToOne
API Platform version(s) affected: 2.6.8
Description
When trying to fetch a subsresource that is a one to one type, the related subresource dataprovider does not get called and a null value is returned in place of the requested field. It works well when the subresource is supposed to be a collection (subresource provider get called and its result is correctly sent)
How to reproduce
- Create two entities (not mapped with doctrine) (A and B for example)
- Add a reference to the other entity from the first one (/** @ApiSubresource */public B $refToB;)
- Create a dataprovider for A entity, not filling at all the $refToB data from the provider
- Execute query in the playground:
query { a { id, refToB { id } } }
=> Exception, $refToB can not be null (or null without error if $refToB was nullable)
Possible Solution
- ApiPlatform\Core\GraphQl\Resolver\Factory\ItemResolverFactory.php:61
// Data already fetched and normalized (field or nested resource) if ($source && \array_key_exists($info->fieldName, $source)) { return $source[$info->fieldName]; }
the array key value exists for $refToB, but it is null, thus we exit this method too early before it has a chance to process anything.
Considering this
if ($source && isset($source[$info->fieldName])) { return $source[$info->fieldName]; }
would solve the execution problem in case of null values.
-
ApiPlatform\Core\GraphQl\Resolver\Stage\ReadStage:86 There is no identifier for the subresource, and we resolved nothing, but the result is returned
// Check for identifier first, then return the result whatever it is if ($identifier) { return $item; }
-
ApiPlatform\Core\GraphQl\Resolver\Stage\ReadStage:98
if (isset($source[$rootProperty = $info->fieldName], $source[ItemNormalizer::ITEM_IDENTIFIERS_KEY], $source[ItemNormalizer::ITEM_RESOURCE_CLASS_KEY]))
=> $source[$rootProperty = $info->fieldName] = false, so we don't enter this condition in case of our $refToB example.
Suggestion is to remove the check for $source[$rootProperty = $info->fieldName] and just leave :
if (isset($source[ItemNormalizer::ITEM_IDENTIFIERS_KEY], $source[ItemNormalizer::ITEM_RESOURCE_CLASS_KEY]))
- ApiPlatform\Core\GraphQl\Resolver\Stage\ReadStage:101-102 (in between)
Add a simple check to verify if we are in a collection query or not :
if (!$context['is_collection']) { return $subresourceCollection; }
otherwise the line below will throw exception as the subresource is not a collection.
Additional Context
I overrid both these files in my project and now ApiSubresource works well with GraphQL and Item/Collection subresource. But in the end i don't know if it is a bug or a desired behaviour in ApiPlatform, nor if the solution provided above is optimum. Could you help us with that ? Thx
Having the same problem. A fix or help would be nice!
On version 2.7+ i found a different issue with subqueries/subquery collections. I made several adjustments into FieldsBuilder.php file to make things work. Again, it looks pretty hacky, and i still don't understand if these things are by design or not. I can share with you the overriden FieldsBuilder.php for version 2.7 that i use if needed.