Skip to content

Commit e6588c8

Browse files
committed
fix: json-api-resource & descriptor support any array or object.
1 parent 0108146 commit e6588c8

File tree

11 files changed

+158
-79
lines changed

11 files changed

+158
-79
lines changed

src/Descriptors/Describer.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Closure;
66
use Illuminate\Http\Request;
77
use Illuminate\Http\Resources\MissingValue;
8+
use Illuminate\Support\Arr;
89

910
/**
1011
* @template T
@@ -100,12 +101,14 @@ protected function check(Request $request, mixed $model, string $attribute): boo
100101

101102
private function retrieveValue(mixed $model, string $attribute): mixed
102103
{
104+
$value = static fn($attr) => Arr::accessible($model) ? $model[$attr] : $model->$attr;
105+
103106
$retriever = $this->retriever();
104107
if ($retriever === null) {
105-
return $model->$attribute;
108+
return $value($attribute);
106109
}
107110
if (is_string($retriever)) {
108-
return $model->$retriever;
111+
return $value($retriever);
109112
}
110113
return $retriever();
111114
}

src/Descriptors/Descriptors.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22

33
namespace Ark4ne\JsonApi\Descriptors;
44

5-
/**
6-
* @deprecated
7-
*/
85
trait Descriptors
96
{
107
use Relations;

src/Descriptors/Relations.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
use Closure;
1717

1818
/**
19-
* @template T as \Illuminate\Database\Eloquent\Model
19+
* @template T
2020
*/
2121
trait Relations
2222
{

src/Descriptors/Relations/Relation.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010
use Illuminate\Database\Eloquent\Model;
1111
use Illuminate\Http\Request;
1212
use Illuminate\Http\Resources\MissingValue;
13+
use Illuminate\Support\Arr;
1314

1415
/**
15-
* @template T as \Illuminate\Database\Eloquent\Model
16+
* @template T
1617
* @extends Describer<T>
1718
*/
1819
abstract class Relation extends Describer
@@ -113,7 +114,11 @@ public function resolveFor(Request $request, mixed $model, string $field): Relat
113114
if ($retriever instanceof Closure) {
114115
$value = static fn() => $retriever($model, $field);
115116
} else {
116-
$value = static fn() => $model->getRelationValue($retriever ?? $field);
117+
$value = static fn() => match (true) {
118+
$model instanceof Model => $model->getRelationValue($retriever ?? $field),
119+
Arr::accessible($model) => $model[$retriever ?? $field],
120+
default => $model->{$retriever ?? $field}
121+
};
117122
}
118123

119124
$relation = $this->value(fn() => $this->check($request, $model, $field) ? $value() : new MissingValue());

src/Descriptors/Relations/RelationMany.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
use Closure;
77

88
/**
9-
* @template T as \Illuminate\Database\Eloquent\Model
9+
* @template T
1010
* @extends Relation<T>
1111
*/
1212
class RelationMany extends Relation

src/Descriptors/Relations/RelationOne.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
use Closure;
77

88
/**
9-
* @template T as \Illuminate\Database\Eloquent\Model
9+
* @template T
1010
* @extends Relation<T>
1111
*/
1212
class RelationOne extends Relation

src/Resources/Concerns/Attributes.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,17 @@ trait Attributes
3030
*/
3131
protected function toAttributes(Request $request): iterable
3232
{
33-
return $this->resource->toArray();
33+
if (is_object($this->resource) && method_exists($this->resource, 'toArray')) {
34+
return $this->resource->toArray();
35+
}
36+
if (is_array($this->resource)) {
37+
return $this->resource;
38+
}
39+
if (is_iterable($this->resource)) {
40+
return iterator_to_array($this->resource);
41+
}
42+
43+
return [];
3444
}
3545

3646
/**

src/Resources/JsonApiCollection.php

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,19 @@ public function __construct($resource, ?string $collects = null)
4545
*/
4646
public function toArray(mixed $request, bool $included = true): array
4747
{
48-
if (collect($this->collection)->every(fn($value) => $value instanceof JsonApiResource)) {
49-
$collection = collect($this->collection);
48+
$collection = collect($this->collection);
5049

50+
if ($collection->every(static fn($value) => $value instanceof JsonApiResource)) {
5151
// @phpstan-ignore-next-line
5252
$loads = array_merge(...$collection->map->requestedRelationshipsLoad($request));
5353

54-
// @phpstan-ignore-next-line
55-
$resources = $collection->map->resource;
54+
if (!empty($loads)) {
55+
// @phpstan-ignore-next-line
56+
$resources = $collection->map->resource;
5657

57-
if (!empty($loads) && $resources->every(fn($resource) => $resource instanceof Model)) {
58-
(new Collection($resources))->loadMissing($loads);
58+
if ($resources->every(static fn($resource) => $resource instanceof Model)) {
59+
(new Collection($resources))->loadMissing($loads);
60+
}
5961
}
6062
}
6163

tests/Unit/Descriptors/RelationTest.php

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,56 +15,69 @@
1515

1616
class RelationTest extends TestCase
1717
{
18-
public function testIncluded()
18+
public function resourceProvider(): array
19+
{
20+
return [
21+
[[]],
22+
[new class {}],
23+
[new class extends Model {}],
24+
];
25+
}
26+
27+
/**
28+
* @dataProvider resourceProvider
29+
*/
30+
public function testIncluded($model)
1931
{
2032
$stub = new RelationOne(UserResource::class, fn() => null);
2133

22-
$relation = $stub->resolveFor(new Request, new class extends Model {
23-
}, 'null');
34+
$relation = $stub->resolveFor(new Request, $model, 'null');
2435

2536
$this->assertInstanceOf(Relationship::class, $relation);
2637
$this->assertFalse(Reflect::get($relation, 'whenIncluded'));
2738

2839
$stub->whenIncluded();
29-
$relation = $stub->resolveFor(new Request, new class extends Model {
30-
}, 'null');
40+
$relation = $stub->resolveFor(new Request, $model, 'null');
3141

3242
$this->assertInstanceOf(Relationship::class, $relation);
3343
$this->assertTrue(Reflect::get($relation, 'whenIncluded'));
3444
}
3545

36-
public function testMeta()
46+
47+
/**
48+
* @dataProvider resourceProvider
49+
*/
50+
public function testMeta($model)
3751
{
3852
$stub = new RelationOne(UserResource::class, fn() => null);
3953

40-
$relation = $stub->resolveFor(new Request, new class extends Model {
41-
}, 'null');
54+
$relation = $stub->resolveFor(new Request, $model, 'null');
4255

4356
$this->assertInstanceOf(Relationship::class, $relation);
4457
$this->assertEmpty(Reflect::get($relation, 'meta'));
4558

4659
$stub->meta(fn() => ['test']);
47-
$relation = $stub->resolveFor(new Request, new class extends Model {
48-
}, 'null');
60+
$relation = $stub->resolveFor(new Request, $model, 'null');
4961

5062
$this->assertInstanceOf(Relationship::class, $relation);
5163
$this->assertInstanceOf(\Closure::class, Reflect::get($relation, 'meta'));
5264
$this->assertEquals(['test'], Reflect::get($relation, 'meta')());
5365
}
5466

55-
public function testLinks()
67+
/**
68+
* @dataProvider resourceProvider
69+
*/
70+
public function testLinks($model)
5671
{
5772
$stub = new RelationOne(UserResource::class, fn() => null);
5873

59-
$relation = $stub->resolveFor(new Request, new class extends Model {
60-
}, 'null');
74+
$relation = $stub->resolveFor(new Request, $model, 'null');
6175

6276
$this->assertInstanceOf(Relationship::class, $relation);
6377
$this->assertEmpty(Reflect::get($relation, 'links'));
6478

6579
$stub->links(fn() => ['test']);
66-
$relation = $stub->resolveFor(new Request, new class extends Model {
67-
}, 'null');
80+
$relation = $stub->resolveFor(new Request, $model, 'null');
6881

6982
$this->assertInstanceOf(Relationship::class, $relation);
7083
$this->assertInstanceOf(\Closure::class, Reflect::get($relation, 'links'));
@@ -120,12 +133,13 @@ public function testWhenPivotLoaded($expectedAttr, $relation, $invokedAttr)
120133
$this->assertTrue($check);
121134
}
122135

123-
public function testRelationMissing()
136+
/**
137+
* @dataProvider resourceProvider
138+
*/
139+
public function testRelationMissing($model)
124140
{
125141
$missing = RelationMissing::fromRelationship(new Relationship(UserResource::class, fn() => null));
126142

127-
$model = new class extends Model {
128-
};
129143
$check = Reflect::invoke($missing, 'check', new Request, $model, 'any');
130144
$value = Reflect::invoke($missing, 'valueFor', new Request, $model, 'any');
131145

0 commit comments

Comments
 (0)