Skip to content

Commit 0557e07

Browse files
committed
[Bugfix] Fix remaining issues with relationship links
1 parent 7df6633 commit 0557e07

File tree

5 files changed

+307
-6
lines changed

5 files changed

+307
-6
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ All notable changes to this project will be documented in this file. This projec
1818

1919
### Fixed
2020

21+
- [#147](https://github.com/laravel-json-api/laravel/issues/147) Related relationship response now correctly merge the
22+
relationship links into the top-level document links member.
2123
- [#130](https://github.com/laravel-json-api/laravel/issues/130) The `JsonApiResource` now correctly handles conditional
2224
fields when iterating over relationships to find a specific relation.
2325
- [#105](https://github.com/laravel-json-api/laravel/issues/105) The JSON:API document returned by a relationship `self`

tests/dummy/tests/Api/V1/Posts/ReadAuthorTest.php

+5-2
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,12 @@ public function test(): void
5151
$response = $this
5252
->withoutExceptionHandling()
5353
->jsonApi('users')
54-
->get(url('/api/v1/posts', [$this->post, 'author']));
54+
->get($related = url('/api/v1/posts', [$this->post, 'author']));
5555

56-
$response->assertFetchedOneExact($expected);
56+
$response->assertFetchedOneExact($expected)->assertLinks([
57+
'self' => url('/api/v1/posts', [$this->post, 'relationships', 'author']),
58+
'related' => $related,
59+
]);
5760
}
5861

5962
public function testFilterMatches(): void

tests/dummy/tests/Api/V1/Posts/ReadCommentsTest.php

+9-4
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,16 @@ public function test(): void
5656
$response = $this
5757
->withoutExceptionHandling()
5858
->jsonApi('comments')
59-
->get(url('/api/v1/posts', [$this->post, 'comments']));
59+
->get($related = url('/api/v1/posts', [$this->post, 'comments']));
6060

61-
$response->assertFetchedMany($expected)->assertExactMeta([
62-
'count' => 3,
63-
]);
61+
$links = [
62+
'self' => url('/api/v1/posts', [$this->post, 'relationships', 'comments']),
63+
'related' => $related,
64+
];
65+
66+
$response->assertFetchedMany($expected)
67+
->assertLinks($links)
68+
->assertExactMeta(['count' => 3]);
6469
}
6570

6671
public function testPaginated(): void
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
<?php
2+
/*
3+
* Copyright 2022 Cloud Creativity Limited
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
declare(strict_types=1);
19+
20+
namespace LaravelJsonApi\Laravel\Tests\Acceptance\Relationships;
21+
22+
use App\JsonApi\V1\Posts\PostSchema;
23+
use App\Models\Post;
24+
use App\Models\Tag;
25+
use Closure;
26+
use LaravelJsonApi\Core\Facades\JsonApi;
27+
use LaravelJsonApi\Laravel\Facades\JsonApiRoute;
28+
use LaravelJsonApi\Laravel\Http\Controllers\JsonApiController;
29+
use LaravelJsonApi\Laravel\Tests\Acceptance\TestCase;
30+
use function url;
31+
32+
class ToManyLinksTest extends TestCase
33+
{
34+
35+
/**
36+
* @var PostSchema
37+
*/
38+
private PostSchema $schema;
39+
40+
/**
41+
* @var Post
42+
*/
43+
private Post $post;
44+
45+
/**
46+
* @var Tag
47+
*/
48+
private Tag $tag;
49+
50+
/**
51+
* @return void
52+
*/
53+
protected function setUp(): void
54+
{
55+
parent::setUp();
56+
57+
JsonApiRoute::server('v1')->prefix('api/v1')->resources(function ($server) {
58+
$server->resource('posts', JsonApiController::class)->relationships(function ($relationships) {
59+
$relationships->hasMany('tags');
60+
});
61+
});
62+
63+
$this->schema = JsonApi::server('v1')->schemas()->schemaFor('posts');
64+
$this->post = Post::factory()->create();
65+
$this->post->tags()->attach($this->tag = Tag::factory()->create());
66+
}
67+
68+
/**
69+
* @return array[]
70+
*/
71+
public function scenarioProvider(): array
72+
{
73+
return [
74+
'hidden' => [
75+
static function (PostSchema $schema) {
76+
$schema->relationship('tags')->hidden();
77+
return null;
78+
},
79+
],
80+
'no links' => [
81+
static function (PostSchema $schema) {
82+
$schema->relationship('tags')->serializeUsing(
83+
static fn($relation) => $relation->withoutLinks()
84+
);
85+
return null;
86+
},
87+
],
88+
'no self link' => [
89+
static function (PostSchema $schema, Post $post) {
90+
$schema->relationship('tags')->serializeUsing(
91+
static fn($relation) => $relation->withoutSelfLink()
92+
);
93+
return ['related' => url('/api/v1/posts', [$post, 'tags'])];
94+
},
95+
],
96+
'no related link' => [
97+
static function (PostSchema $schema, Post $post) {
98+
$schema->relationship('tags')->serializeUsing(
99+
static fn($relation) => $relation->withoutRelatedLink()
100+
);
101+
return ['self' => url('/api/v1/posts', [$post, 'relationships', 'tags'])];
102+
},
103+
],
104+
];
105+
}
106+
107+
/**
108+
* @param Closure $scenario
109+
* @return void
110+
* @dataProvider scenarioProvider
111+
*/
112+
public function testRelated(Closure $scenario): void
113+
{
114+
$expected = $scenario($this->schema, $this->post);
115+
116+
$response = $this
117+
->withoutExceptionHandling()
118+
->jsonApi('tags')
119+
->get(url('/api/v1/posts', [$this->post, 'tags']));
120+
121+
$response->assertFetchedMany([$this->tag]);
122+
123+
if (is_array($expected)) {
124+
$response->assertLinks($expected);
125+
}
126+
}
127+
128+
/**
129+
* @param Closure $scenario
130+
* @return void
131+
* @dataProvider scenarioProvider
132+
*/
133+
public function testSelf(Closure $scenario): void
134+
{
135+
$expected = $scenario($this->schema, $this->post);
136+
137+
$response = $this
138+
->withoutExceptionHandling()
139+
->jsonApi('tags')
140+
->get(url('/api/v1/posts', [$this->post, 'relationships', 'tags']));
141+
142+
$response->assertFetchedToMany([$this->tag]);
143+
144+
if (is_array($expected)) {
145+
$response->assertLinks($expected);
146+
}
147+
}
148+
149+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
<?php
2+
/*
3+
* Copyright 2022 Cloud Creativity Limited
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
declare(strict_types=1);
19+
20+
namespace LaravelJsonApi\Laravel\Tests\Acceptance\Relationships;
21+
22+
use App\JsonApi\V1\Posts\PostSchema;
23+
use App\Models\Post;
24+
use Closure;
25+
use LaravelJsonApi\Core\Facades\JsonApi;
26+
use LaravelJsonApi\Laravel\Facades\JsonApiRoute;
27+
use LaravelJsonApi\Laravel\Http\Controllers\JsonApiController;
28+
use LaravelJsonApi\Laravel\Tests\Acceptance\TestCase;
29+
use function url;
30+
31+
class ToOneLinksTest extends TestCase
32+
{
33+
34+
/**
35+
* @var PostSchema
36+
*/
37+
private PostSchema $schema;
38+
39+
/**
40+
* @var Post
41+
*/
42+
private Post $post;
43+
44+
/**
45+
* @return void
46+
*/
47+
protected function setUp(): void
48+
{
49+
parent::setUp();
50+
51+
JsonApiRoute::server('v1')->prefix('api/v1')->resources(function ($server) {
52+
$server->resource('posts', JsonApiController::class)->relationships(function ($relationships) {
53+
$relationships->hasOne('author');
54+
});
55+
});
56+
57+
$this->schema = JsonApi::server('v1')->schemas()->schemaFor('posts');
58+
$this->post = Post::factory()->create();
59+
}
60+
61+
/**
62+
* @return array[]
63+
*/
64+
public function scenarioProvider(): array
65+
{
66+
return [
67+
'hidden' => [
68+
static function (PostSchema $schema) {
69+
$schema->relationship('author')->hidden();
70+
return null;
71+
},
72+
],
73+
'no links' => [
74+
static function (PostSchema $schema) {
75+
$schema->relationship('author')->serializeUsing(
76+
static fn($relation) => $relation->withoutLinks()
77+
);
78+
return null;
79+
},
80+
],
81+
'no self link' => [
82+
static function (PostSchema $schema, Post $post) {
83+
$schema->relationship('author')->serializeUsing(
84+
static fn($relation) => $relation->withoutSelfLink()
85+
);
86+
return ['related' => url('/api/v1/posts', [$post, 'author'])];
87+
},
88+
],
89+
'no related link' => [
90+
static function (PostSchema $schema, Post $post) {
91+
$schema->relationship('author')->serializeUsing(
92+
static fn($relation) => $relation->withoutRelatedLink()
93+
);
94+
return ['self' => url('/api/v1/posts', [$post, 'relationships', 'author'])];
95+
},
96+
],
97+
];
98+
}
99+
100+
/**
101+
* @param Closure $scenario
102+
* @return void
103+
* @dataProvider scenarioProvider
104+
*/
105+
public function testRelated(Closure $scenario): void
106+
{
107+
$expected = $scenario($this->schema, $this->post);
108+
109+
$response = $this
110+
->withoutExceptionHandling()
111+
->jsonApi('users')
112+
->get(url('/api/v1/posts', [$this->post, 'author']));
113+
114+
$response->assertFetchedOne($this->post->author);
115+
116+
if (is_array($expected)) {
117+
$response->assertLinks($expected);
118+
}
119+
}
120+
121+
/**
122+
* @param Closure $scenario
123+
* @return void
124+
* @dataProvider scenarioProvider
125+
*/
126+
public function testSelf(Closure $scenario): void
127+
{
128+
$expected = $scenario($this->schema, $this->post);
129+
130+
$response = $this
131+
->withoutExceptionHandling()
132+
->jsonApi('users')
133+
->get(url('/api/v1/posts', [$this->post, 'relationships', 'author']));
134+
135+
$response->assertFetchedToOne($this->post->author);
136+
137+
if (is_array($expected)) {
138+
$response->assertLinks($expected);
139+
}
140+
}
141+
142+
}

0 commit comments

Comments
 (0)