Skip to content

Commit 5b3d39c

Browse files
committed
Merge branch 'release/4.1.0'
2 parents 5dbf042 + 7e4e547 commit 5b3d39c

File tree

7 files changed

+161
-5
lines changed

7 files changed

+161
-5
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file. This projec
55

66
## Unreleased
77

8+
## [4.1.0] - 2024-06-26
9+
10+
### Fixed
11+
12+
- [#17](https://github.com/laravel-json-api/core/pull/17) Fix incorrect `self` link in related resource responses, and
13+
remove `related` link that should not exist. This has been incorrect for some time, but is definitely what
14+
the [spec defines here.](https://jsonapi.org/format/1.0/#document-top-level)
15+
816
## [4.0.0] - 2024-03-12
917

1018
### Changed

src/Core/Document/Links.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,25 @@ public function hasRelated(): bool
191191
return $this->has('related');
192192
}
193193

194+
/**
195+
* @return $this
196+
*/
197+
public function relatedAsSelf(): self
198+
{
199+
$related = $this->getRelated();
200+
201+
if ($related) {
202+
$this->push(new Link(
203+
key: 'self',
204+
href: $related->href(),
205+
meta: $related->meta(),
206+
));
207+
return $this->forget('related');
208+
}
209+
210+
return $this->forget('self');
211+
}
212+
194213
/**
195214
* Push links into the collection.
196215
*

src/Core/Responses/Internal/RelatedResourceCollectionResponse.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Illuminate\Contracts\Support\Responsable;
1515
use Illuminate\Http\Request;
1616
use Illuminate\Http\Response;
17+
use LaravelJsonApi\Core\Document\Links;
1718
use LaravelJsonApi\Core\Resources\JsonApiResource;
1819
use LaravelJsonApi\Core\Responses\Concerns\HasEncodingParameters;
1920
use LaravelJsonApi\Core\Responses\Concerns\HasRelationship;
@@ -68,4 +69,17 @@ public function toResponse($request)
6869
$this->headers()
6970
);
7071
}
72+
73+
/**
74+
* Get all links.
75+
*
76+
* @return Links
77+
*/
78+
private function allLinks(): Links
79+
{
80+
return $this
81+
->linksForRelationship()
82+
->relatedAsSelf()
83+
->merge($this->links());
84+
}
7185
}

src/Core/Responses/Internal/RelatedResourceResponse.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Illuminate\Contracts\Support\Responsable;
1515
use Illuminate\Http\Request;
1616
use Illuminate\Http\Response;
17+
use LaravelJsonApi\Core\Document\Links;
1718
use LaravelJsonApi\Core\Resources\JsonApiResource;
1819
use LaravelJsonApi\Core\Responses\Concerns\HasEncodingParameters;
1920
use LaravelJsonApi\Core\Responses\Concerns\HasRelationship;
@@ -52,6 +53,7 @@ public function toResponse($request)
5253
{
5354
$encoder = $this->server()->encoder();
5455

56+
$links = $this->allLinks();
5557
$document = $encoder
5658
->withRequest($request)
5759
->withIncludePaths($this->includePaths($request))
@@ -68,4 +70,17 @@ public function toResponse($request)
6870
$this->headers()
6971
);
7072
}
73+
74+
/**
75+
* Get all links.
76+
*
77+
* @return Links
78+
*/
79+
private function allLinks(): Links
80+
{
81+
return $this
82+
->linksForRelationship()
83+
->relatedAsSelf()
84+
->merge($this->links());
85+
}
7186
}

src/Core/Schema/Concerns/MatchesIds.php

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
trait MatchesIds
1515
{
16-
1716
/**
1817
* @var string
1918
*/
@@ -39,7 +38,7 @@ public function pattern(): string
3938
*
4039
* @return $this
4140
*/
42-
public function uuid(): self
41+
public function uuid(): static
4342
{
4443
return $this->matchAs('[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}');
4544
}
@@ -49,7 +48,7 @@ public function uuid(): self
4948
*
5049
* @return $this
5150
*/
52-
public function ulid(): self
51+
public function ulid(): static
5352
{
5453
return $this->matchAs('[0-7][0-9a-hjkmnp-tv-zA-HJKMNP-TV-Z]{25}');
5554
}
@@ -60,7 +59,7 @@ public function ulid(): self
6059
* @param string $pattern
6160
* @return $this
6261
*/
63-
public function matchAs(string $pattern): self
62+
public function matchAs(string $pattern): static
6463
{
6564
$this->pattern = $pattern;
6665

@@ -72,7 +71,7 @@ public function matchAs(string $pattern): self
7271
*
7372
* @return $this
7473
*/
75-
public function matchCase(): self
74+
public function matchCase(): static
7675
{
7776
$this->flags = 'D';
7877

tests/Unit/Document/LinksTest.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,4 +294,23 @@ public function testOffsetUnset(): void
294294

295295
$this->assertSame(['related' => $related], $links->all());
296296
}
297+
298+
public function testRelatedToSelfWithRelated(): void
299+
{
300+
$links = new Links(
301+
new Link('self', '/api/posts/1/relationships/author'),
302+
new Link('related', '/api/posts/1/author'),
303+
);
304+
305+
$this->assertEquals(['self' => new Link('self', '/api/posts/1/author'),], $links->relatedAsSelf()->all());
306+
}
307+
308+
public function testRelatedToSelfWithoutRelated(): void
309+
{
310+
$links = new Links(
311+
new Link('self', '/api/posts/1/relationships/author'),
312+
);
313+
314+
$this->assertTrue($links->relatedAsSelf()->isEmpty());
315+
}
297316
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
/*
3+
* Copyright 2024 Cloud Creativity Limited
4+
*
5+
* Use of this source code is governed by an MIT-style
6+
* license that can be found in the LICENSE file or at
7+
* https://opensource.org/licenses/MIT.
8+
*/
9+
10+
declare(strict_types=1);
11+
12+
namespace LaravelJsonApi\Core\Tests\Unit\Schema\Concerns;
13+
14+
use LaravelJsonApi\Core\Schema\Concerns\MatchesIds;
15+
use PHPUnit\Framework\TestCase;
16+
17+
class MatchesIdsTest extends TestCase
18+
{
19+
/**
20+
* @return void
21+
*/
22+
public function testItIsNumeric(): void
23+
{
24+
$id = new class () {
25+
use MatchesIds;
26+
};
27+
28+
$this->assertSame('[0-9]+', $id->pattern());
29+
$this->assertTrue($id->match('1234'));
30+
$this->assertFalse($id->match('123A45'));
31+
}
32+
33+
/**
34+
* @return void
35+
*/
36+
public function testItIsUuid(): void
37+
{
38+
$id = new class () {
39+
use MatchesIds;
40+
};
41+
42+
$this->assertSame($id, $id->uuid());
43+
$this->assertSame('[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}', $id->pattern());
44+
$this->assertTrue($id->match('fca1509e-9178-45fd-8a2b-ae819d34f7e6'));
45+
$this->assertFalse($id->match('fca1509e917845fd8a2bae819d34f7e6'));
46+
}
47+
48+
/**
49+
* @return void
50+
*/
51+
public function testItIsUlid(): void
52+
{
53+
$id = new class () {
54+
use MatchesIds;
55+
};
56+
57+
$this->assertSame($id, $id->ulid());
58+
$this->assertSame('[0-7][0-9a-hjkmnp-tv-zA-HJKMNP-TV-Z]{25}', $id->pattern());
59+
$this->assertTrue($id->match('01HT4PA8AZC8Q30ZGC5PEWZP0E'));
60+
$this->assertFalse($id->match('01HT4PA8AZC8Q30ZGC5PEWZP0'));
61+
}
62+
63+
/**
64+
* @return void
65+
*/
66+
public function testItIsCustom(): void
67+
{
68+
$id = new class () {
69+
use MatchesIds;
70+
};
71+
72+
$this->assertSame($id, $id->matchAs('[A-D]+'));
73+
$this->assertSame('[A-D]+', $id->pattern());
74+
$this->assertTrue($id->match('abcd'));
75+
$this->assertTrue($id->match('ABCD'));
76+
$this->assertFalse($id->match('abcde'));
77+
78+
$this->assertSame($id, $id->matchCase());
79+
$this->assertTrue($id->match('ABCD'));
80+
$this->assertFalse($id->match('abcd'));
81+
}
82+
}

0 commit comments

Comments
 (0)