api-platform / core

The server component of API Platform: hypermedia and GraphQL APIs in minutes
https://api-platform.com
MIT License
2.44k stars 868 forks source link

Tree : use IRI instead of normalization context #1130

Closed pierre-H closed 6 years ago

pierre-H commented 7 years ago

Hello !

I've got an entity with a tree :

/**
 * @ApiResource(
 *     itemOperations={
 *          "get"={"method"="GET", "normalization_context"={"groups"={"read"}}},
 *     }
 *  )
 */
class Comment {
    /**
     * @var string
     * @Groups({"read"})
     */
    private $title;

    /**
     * @var Comment
     * @Groups({"read"})
     */
    private $parent;
}

I'd like to have the IRI parent, not the content. The problem here is that I get :

{
    "title": "My first post",
    "parent": {
        "title": "My second post"
    }
}

instead of :

{
    "title": "My first post",
    "parent": "/posts/2"
}

I know that there is the @MaxDepth annotation, but it doesn't generate IRI. Is there another solution ?

soyuka commented 7 years ago

Dooh, I had the same issue yesterday. The solution would be to implement the circular_reference_handler configuration in symfony.

I can't give you my implementation because I use another normalization implementation :/.

pierre-H commented 7 years ago

@soyuka circular_reference_handler is when the serializer finds a circle. But in my case, there is no circle.

soyuka commented 7 years ago

Oh my bad, thought you had children too. For this to work you need to use some kind of dynamic group.

Or maybe we're missing something for the MaxDepth implementation to do what you said. I'll look into this because I also expected that result and it didn't happend.

Another solution is to use some kind of dynamic group. Indeed you serialize with read on Comment and because parent is also an instance of Comment you'll get the same fields.

teohhanhui commented 7 years ago

Related (same?): api-platform/core#1001

pierre-H commented 7 years ago

@teohhanhui Yes you're right : it's the same question. I still don't have a good solution for this ...

teohhanhui commented 7 years ago

Unfortunately, I don't have a good solution for this either... We'll need to put our minds (and fingers) together to come up with something...

silverbackdan commented 6 years ago

This can be closed as https://github.com/api-platform/core/pull/1528 is merged

pierre-H commented 6 years ago

Since alwaysIdentifier` doesn't exist now and the Max Depth Handler is only a function and there is no parameter, how can we solve this problem ? Thanks !

pierre-H commented 6 years ago

For history : @dunglas Added alwaysIdentifier which was a great feature : #1528 Then @dunglar reverted in #1696 for https://github.com/symfony/symfony/pull/26108 but it was closed and we don't know what to do ...

silverbackdan commented 6 years ago

You may be looking for https://symfony.com/blog/new-in-symfony-4-1-serializer-improvements#added-a-maxdepth-handler

It can be implemented now with the latest Symfony Serializer.

pierre-H commented 6 years ago

Yes but the question is how do I add it to AP ?

silverbackdan commented 6 years ago

Write a custom normalizer https://api-platform.com/docs/core/content-negotiation/#writing-a-custom-normalizer

and set the handler

$normalizer->setMaxDepthHandler(function ($foo) {
    return '/foos/'.$foo->id;
});
pierre-H commented 6 years ago

Ok, thanks @silverbackdan

silverbackdan commented 6 years ago

@pierre-H No problem, happy to help!

silverbackdan commented 6 years ago

Also, For a generic example to get the IRI you may want to inject the IriConverter class https://github.com/api-platform/core/blob/master/src/Bridge/Symfony/Routing/IriConverter.php

...
use ApiPlatform\Core\Bridge\Symfony\Routing\IriConverter;
...
public function __construct(NormalizerInterface $normalizer, IriConverter $iriConverter)
{
    ...
    $normalizer->setMaxDepthHandler(function ($foo) use ($iriConverter) {
        return $iriConverter->getIriFromItem($foo);
    });
    $this->normalizer = $normalizer;
}

Very rough just to give you and others a general idea but hopefully you get the gist.

ghettovoice commented 6 years ago

Hi for all! Is the last solution with setMaxDepthHandler still actual? I tried it but with no luck. The handler gets array instead of object as first argument, obviously due to maxDepthHandler invoked after relation normalization:

As a result: I can't use $iriConverter->getIriFromItem($object) because it expected resource object.

Also I have an issue with maxDepth, I want set it to 1 (to get parent from example above as IRI on the first level) but it doesn't work because the first invocation of isMaxDepthReached always returns false and parent always embedded on the first level. https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php#L423. Maybe this should be asked on the symfony main repository, I just want to know is it relevant or I'm doing something wrong. Who ever encountered this?

Thanks!