neomerx / json-api

Framework agnostic JSON API (jsonapi.org) implementation

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Related resources of a relationship endpoint

jpoelmans opened this issue · comments

The Jsonapi specification states:

Furthermore, related resources can be requested from a relationship endpoint:

GET /articles/1/relationships/comments?include=comments.author HTTP/1.1
Accept: application/vnd.api+json

In this case, the primary data would be a collection of resource identifier objects that represent linkage to comments for an article, while the full comments and comment authors would be returned as included data.

But I can't get this working. Are includes on Resource Identifiers possible?

It seems to be a case nobody tried before 🎆

Currently, if you encode with identifiers you will get only identifiers and includes will be ignored. Here is a sample how it works (note despite EncodingParameters has include for author it is ignored)

    public function testEncodeObjectAsResourceIdentityWithRelationships(): void
    {
        $comment = Comment::instance(1, 'One!');
        $author  = Author::instance(9, 'Dan', 'Gebhardt', [$comment]);
        $comment->{Comment::LINK_AUTHOR} = $author;

        $encoder = Encoder::instance([
            Author::class  => AuthorSchema::class,
            Comment::class => CommentSchema::class,
        ], $this->encoderOptions);

        $actual = $encoder->encodeIdentifiers($comment, new EncodingParameters(
           // include `author` relationship
            [Comment::LINK_AUTHOR]
        ));

        $expected = <<<EOL
        {
            "data" : {
                "type" : "comments",
                "id"   : "1"
            }
        }
EOL;
        // remove formatting from 'expected'
        $expected = json_encode(json_decode($expected));

        $this->assertEquals($expected, $actual);
    }

It looks like if include paths are given then encoder should put full resources into included.

Currently, the closest you can get is replacing encodeIdentifiers with encodeData. In this case you will get full comments resources in data section with authors included.

Thanks for the response.

Using encodeData is a solution for now, but it is not in compliance with the specification! Can this be a feature request for a next version?

Btw there's a PHPUnit assertion assertJsonStringEqualsJsonString that can be used instead of doing that json_encode(json_decode( trick.

@jpoelmans Before this point, it wasn't the case when we have the same resource in main data (type+id) and included (full resource). So it's likely to be more than just a small change. I'll keep this issue open as a task for the next major release.

I've got some update on this. I'm currently working on version 3 of the library. Currently, it's it is a promising prototype which can run the sample app (including performance test) and 75% of basic encoding tests (from EncodeSimpleObjectsTest.php). So far performance is about +30% and it can encode correclty the test above

    /**
     * Test encode simple object as resource identity with included resources.
     */
    public function testEncodeObjectAsResourceIdentityWithIncludes(): void
    {
        $comment = Comment::instance(1, 'One!');
        $author  = Author::instance(9, 'Dan', 'Gebhardt', [$comment]);
        $comment->{Comment::LINK_AUTHOR} = $author;

        $actual = Encoder::instance([
            Author::class  => AuthorSchema::class,
            Comment::class => CommentSchema::class,
        ])
            ->withUrlPrefix('http://example.com')
            ->withIncludedPaths([Comment::LINK_AUTHOR])
            ->encodeIdentifiers($comment);

        $expected = <<<EOL
        {
            "data" : {
                "type" : "comments",
                "id"   : "1"
            },
            "included": [
                {
                    "type" : "comments",
                    "id"   : "1",
                    "attributes": {
                        "body": "One!"
                    },
                    "relationships": {
                        "author": {
                            "data": {
                                "type" : "people",
                                "id"   : "9"
                            }
                        }
                    },
                    "links": {
                        "self": "http://example.com/comments/1"
                    }
                }, {
                    "type" : "people",
                    "id"   : "9",
                    "attributes": {
                        "first_name" : "Dan",
                        "last_name"  : "Gebhardt"
                    },
                    "relationships": {
                        "comments": {
                            "data": [
                                { "type" : "comments", "id"   : "1" }
                            ]
                        }
                    },
                    "links": {
                        "self": "http://example.com/people/9"
                    }
                }
            ]
        }
EOL;

        self::assertJsonStringEqualsJsonString($expected, $actual);
    }

BTW, @lindyhopchris thanks for assertJsonStringEqualsJsonString it does look much better in IDE when comparing those jsons.

The issue is resolved in the next version of the library which is currently published in branch next. Later it will be moved to develop and then merged to master.

merged to develop

released as v3.0.0