api-platform icon indicating copy to clipboard operation
api-platform copied to clipboard

Subresource with GraphQL (without Doctrine support) only yields results for oneToMany relationships, returns null for oneToOne

Open jav974 opened this issue 3 years ago • 2 comments

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

  1. Create two entities (not mapped with doctrine) (A and B for example)
  2. Add a reference to the other entity from the first one (/** @ApiSubresource */public B $refToB;)
  3. Create a dataprovider for A entity, not filling at all the $refToB data from the provider
  4. 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

  1. 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.

  1. 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; }

  2. 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]))

  1. 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

jav974 avatar Feb 09 '22 08:02 jav974

Having the same problem. A fix or help would be nice!

JE4GLE avatar Apr 10 '23 21:04 JE4GLE

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.

jav974 avatar Jul 10 '23 07:07 jav974