Skip to content

Commit 7de94fd

Browse files
authored
fix: assign missing value when a relation resource is null (#20)
* fix: assign missing value when a relation resource is null * fix: phpstan * fix: phpstan * fix: phpstan
1 parent fbfaf07 commit 7de94fd

File tree

8 files changed

+63
-1
lines changed

8 files changed

+63
-1
lines changed

src/Resources/Relationship.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,8 @@ public function toArray(mixed $request, bool $included = true): array
145145
{
146146
$value = $this->whenIncluded && !$included
147147
? new MissingValue
148-
: value($this->value);
148+
: value($this->value)
149+
?? new MissingValue;
149150

150151
if ($this->asCollection && !is_subclass_of($this->resource, ResourceCollection::class)) {
151152
$resource = $this->resource::collection($value);

src/Support/Values.php

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public static function mergeValues(iterable $data): array
2929
$index++;
3030

3131
if (is_iterable($value)) {
32+
/** @var array<array-key, mixed> $value */
3233
$data[$key] = self::mergeValues($value);
3334

3435
continue;

tests/Feature/SchemaTest.php

+2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ public function testSchema()
1717
$post = new Skeleton(PostResource::class, 'post', ['state', 'title', 'content']);
1818
$comment = new Skeleton(CommentResource::class, 'comment', ['content']);
1919

20+
$user->relationships['main-post'] = $post;
2021
$user->relationships['posts'] = $post;
2122
$user->relationships['comments'] = $comment;
23+
$user->loads['main-post'] = 'post';
2224
$user->loads['posts'] = 'posts';
2325
$user->loads['comments'] = [
2426
'comments' => fn(Builder $q) => $q->where('content', 'like', '%e%')

tests/Feature/User/ResourceTest.php

+1
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ private function getJsonResult(User $user, ?array $attributes = null, ?array $re
166166
'email' => $user->email,
167167
], array_fill_keys($attributes ?? ['name', 'email'], true))),
168168
'relationships' => [
169+
'main-post' => [],
169170
'posts' => array_filter([
170171
'data' => $withIncluded ? $user->posts->map(fn(Post $post) => ['type' => 'post', 'id' => $post->id])->all() : null,
171172
'links' => [

tests/Unit/Resources/RelationshipTest.php

+33
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,23 @@ public function testToArrayMissingValue()
144144
], $relation->toArray(new Request(), false));
145145
}
146146

147+
public function testToArrayNullValue()
148+
{
149+
$resource = $this->getJsonResourceNullValue();
150+
$relation = (new Relationship($resource::class, fn() => $resource->resource))->withLinks(fn() => [
151+
'self' => 'link'
152+
])->withMeta(fn() => [
153+
'hash' => 'azerty'
154+
]);
155+
156+
$this->assertEquals([
157+
'data' => [
158+
'links' => ['self' => 'link'],
159+
'meta' => ['hash' => 'azerty'],
160+
],
161+
], $relation->toArray(new Request(), false));
162+
}
163+
147164
public function testCustomValue()
148165
{
149166
$relation = (new Relationship(Collection::class, fn() => ['foo' => 'bar']))
@@ -217,6 +234,22 @@ public function toArray($request, bool $minimal = false): array
217234
};
218235
}
219236

237+
private function getJsonResourceNullValue()
238+
{
239+
return new class(null) extends JsonResource implements Resourceable {
240+
public function toArray($request, bool $minimal = false): array
241+
{
242+
return [
243+
'type' => 'my-model',
244+
'id' => $this->id,
245+
'attributes' => [
246+
'foo' => 'bar'
247+
]
248+
];
249+
}
250+
};
251+
}
252+
220253
private function getResourceCollection()
221254
{
222255
return new class(collect()) extends ResourceCollection implements Resourceable {

tests/app/Http/Resources/UserResource.php

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ protected function toResourceMeta(Request $request): ?iterable
3939
protected function toRelationships(Request $request): iterable
4040
{
4141
return [
42+
'main-post' => $this->one(PostResource::class, fn() => $this->resource->post)
43+
->withLoad('post'),
4244
'posts' => PostResource::relationship(fn() => $this->resource->posts, fn() => [
4345
'self' => "https://api.example.com/user/{$this->resource->id}/relationships/posts",
4446
'related' => "https://api.example.com/user/{$this->resource->id}/posts",

tests/app/Models/User.php

+7
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Illuminate\Database\Eloquent\Factories\HasFactory;
66
use Illuminate\Database\Eloquent\Model;
7+
use Illuminate\Database\Eloquent\Relations\BelongsTo;
78
use Illuminate\Database\Eloquent\Relations\HasMany;
89
use Test\app\Factories\UserFactory;
910

@@ -14,6 +15,7 @@
1415
* @property \DateTimeInterface $created_at
1516
* @property \DateTimeInterface $updated_at
1617
*
18+
* @property-read Post|null $post
1719
* @property-read Post[]|\Illuminate\Support\Collection<Post> $posts
1820
* @property-read Comment[]|\Illuminate\Support\Collection<Comment> $comments
1921
*/
@@ -26,6 +28,11 @@ protected static function newFactory(): UserFactory
2628
return new UserFactory();
2729
}
2830

31+
public function post(): BelongsTo
32+
{
33+
return $this->belongsTo(Post::class);
34+
}
35+
2936
public function posts(): HasMany
3037
{
3138
return $this->hasMany(Post::class);

tests/app/migrations.php

+15
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,26 @@ public function tables()
3636
];
3737
}
3838

39+
public function changes()
40+
{
41+
return [
42+
'users' => function (Blueprint $table) {
43+
$table->foreignId('post_id')
44+
->nullable()
45+
->after('password')
46+
->constrained('posts');
47+
},
48+
];
49+
}
50+
3951
public function up()
4052
{
4153
foreach ($this->tables() as $table => $blueprint) {
4254
Schema::create($table, $blueprint);
4355
}
56+
foreach ($this->changes() as $table => $blueprint) {
57+
Schema::table($table, $blueprint);
58+
}
4459
}
4560

4661
public function down()

0 commit comments

Comments
 (0)