Skip to content

Incorrect Operation context when serializing nested ApiResource properties #5704

Open
@jonnyeom

Description

@jonnyeom

API Platform version(s) affected: 2.7(with new metadata), 3.0, 3.1/main

Description
Hello,
I am not sure if this is necessarily a bug, or a misunderstanding on the approach on my part.

While I was upgrading a large project from api-platform 2.6 >> 3.x, I am running into an interesting issue during serialization.

Example
Here is an example to explain the situation.

  • Lets say I have an ApiResource Car.
    • Car has a referenced property Wheel. This can be a collection. But Wheel is not an ApiResource.
      • Wheel has a referenced property Tire which is an ApiResource.

You can see in this situation, The nesting of entities to serialize is Resource object > non-Resource object > Resource object.

When you serialize Car, its serialization context bubbles down all the way to Wheel and to Tire.

  • When serializing Car $context['operation'] is set to Car. This is passed down to Wheel.
    • When serializing Wheel, $context['operation'] is still set to Car. But its not used in the AbstractObjectNormalizer, so simply passed on
      • When serializing Tire, $context['operation'] is still set to Car. AbstractItemNormalizer will look at the passed $context['operation'] and will not try to determine the correct operation for Tire. This is where the issue happens.

The biggest culprit ive run into is \ApiPlatform\Symfony\Routing\IriConverter::getIriFromResource() uses the given $operation parameter to generate an iri. But with the wrong context, it will throw an Exception saying Unable to generate an IRI for the item of type

Possible Solution

  1. unset the $context['operation'] each time we go into a new nested level (Perhaps at the $childContext level).
    \ApiPlatform\Serializer\AbstractItemNormalizer::getAttributeValue seems to generate the $context['operation'] whenever it needs one.
    If going with this approach, unsetting the operation context towards the beginning of ::getAttributeValue seems to fix this issue, although I have not fully tested it.

  2. Make everything an ApiResource.

This is another way to solve this does not seem right. If I mark every nested property of an ApiResource class also as an ApiResource, I will not run into problems. But I would have to alter the generated Api docs for each class. I would also really be mis-using the ApiResource attribute for simple serialization purposes.

Bug vs Documentation
Of the two approaches above,
If the intention with 3.x is for every class needing to be marked as ApiResource— This is not a bug. I think additional documentation to make it clearer would help.

If that is not the intention,
Than I will definitely start working on a clean PR for this.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions