Skip to content

Commit 8de2598

Browse files
committed
fix: json-api-resource return true deep array
1 parent da4c761 commit 8de2598

File tree

7 files changed

+363
-17
lines changed

7 files changed

+363
-17
lines changed

CHANGELOG-1.1.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
Release note
22
============
33

4+
# v1.1.8
5+
### Changes
6+
- **@deprecated** `Support\With` is replaced by `Support\Arr`
7+
8+
### Fixes
9+
- `JsonApiResource::toArray` return true deep array
10+
411
# v1.1.7
512
### Fixes
613
- Request rules: fix `$failures` not initialized

src/Resources/JsonApiCollection.php

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

33
namespace Ark4ne\JsonApi\Resources;
44

5-
use Ark4ne\JsonApi\Support\With;
5+
use Ark4ne\JsonApi\Support\Arr;
66
use Illuminate\Http\Resources\Json\JsonResource;
77
use Illuminate\Http\Resources\Json\ResourceCollection;
8-
use Illuminate\Support\Collection;
98

109
/**
1110
* @template T as JsonApiResource
@@ -46,18 +45,18 @@ public function toArray(mixed $request, bool $included = true): array
4645
{
4746
$data = [];
4847

49-
$base = (new Collection($this->with))->toArray();
48+
$base = $this->with;
5049
foreach ($this->collection as $resource) {
5150
$data[] = $resource->toArray($request, $included);
5251

5352
if ($resource instanceof JsonResource) {
54-
$with = (new Collection($resource->with($request)))->toArray();
53+
$with = $resource->with($request);
5554

5655
if (!$included) {
5756
unset($with['included']);
5857
}
5958

60-
$base = With::merge($base, $with);
59+
$base = Arr::merge($base, $with);
6160
}
6261
}
6362
$this->with = $base;
@@ -72,6 +71,6 @@ public function toArray(mixed $request, bool $included = true): array
7271
*/
7372
public function with($request): array
7473
{
75-
return With::wash($this->with);
74+
return Arr::wash($this->with);
7675
}
7776
}

src/Resources/JsonApiResource.php

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
namespace Ark4ne\JsonApi\Resources;
44

55
use Ark4ne\JsonApi\Resources\Concerns;
6-
use Ark4ne\JsonApi\Support\With;
6+
use Ark4ne\JsonApi\Support\Arr;
77
use Illuminate\Http\Resources\Json\JsonResource;
8-
use Illuminate\Support\Collection;
98

109
/**
1110
* @template T
@@ -47,7 +46,7 @@ public function toArray(mixed $request, bool $included = true): array
4746
];
4847
}
4948

50-
return array_filter($data);
49+
return Arr::toArray(array_filter($data));
5150
}
5251

5352
/**
@@ -57,13 +56,13 @@ public function toArray(mixed $request, bool $included = true): array
5756
*/
5857
public function with($request): array
5958
{
60-
$with = new Collection($this->with);
59+
$with = $this->with;
6160

6261
if ($meta = $this->toMeta($request)) {
63-
$with = With::merge($with, ['meta' => $meta]);
62+
$with = Arr::merge($with, ['meta' => $meta]);
6463
}
6564

66-
return With::wash($with);
65+
return Arr::wash($with);
6766
}
6867

6968
/**

src/Support/Arr.php

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
<?php
2+
3+
namespace Ark4ne\JsonApi\Support;
4+
5+
use Illuminate\Contracts\Support\Arrayable;
6+
use Illuminate\Support\Arr as SupportArr;
7+
use Illuminate\Support\Collection;
8+
9+
class Arr
10+
{
11+
/**
12+
* @template TKey as array-key
13+
* @template TValue
14+
*
15+
* @param iterable<TKey, TValue> $iterator
16+
*
17+
* @return array<TKey, TValue>
18+
*/
19+
public static function toArray(iterable $iterator): array
20+
{
21+
return (new Collection($iterator))->map(function ($value) {
22+
if ($value instanceof Arrayable) {
23+
$value = $value->toArray();
24+
}
25+
26+
return is_array($value) ? self::toArray($value) : $value;
27+
})->all();
28+
}
29+
30+
/**
31+
* @template TKey as array-key
32+
* @template UKey as array-key
33+
* @template TValue
34+
* @template UValue
35+
*
36+
* @param iterable<TKey, TValue> $base
37+
* @param iterable<UKey, UValue> $with
38+
*
39+
* @return array<TKey | UKey, TValue | UValue>
40+
*/
41+
public static function merge(iterable $base, iterable $with): array
42+
{
43+
$base = self::toArray($base);
44+
45+
foreach (self::toArray($with) as $key => $value) {
46+
$base[$key] = array_merge_recursive(
47+
$base[$key] ?? [],
48+
$value
49+
);
50+
}
51+
52+
return self::uniqueRecursive($base);
53+
}
54+
55+
/**
56+
* @template TKey as array-key
57+
* @template TValue
58+
*
59+
* @param iterable<TKey, TValue> $with
60+
*
61+
* @return array<TKey, TValue>
62+
*/
63+
public static function wash(iterable $with): array
64+
{
65+
return array_filter(self::uniqueRecursive(self::toArray($with)));
66+
}
67+
68+
/**
69+
* @template TKey as array-key
70+
* @template TValue
71+
*
72+
* @param array<TKey, TValue> $value
73+
*
74+
* @return array<TKey, TValue>
75+
*/
76+
private static function uniqueRecursive(array $value): array
77+
{
78+
return array_map(
79+
static fn($value) => is_array($value)
80+
? self::uniqueRecursive(self::uniqueKeyPreserved($value))
81+
: $value,
82+
$value
83+
);
84+
}
85+
86+
/**
87+
* @template TKey as array-key
88+
* @template TValue
89+
*
90+
* @param array<TKey, TValue> $value
91+
*
92+
* @return array<TKey, TValue>
93+
*/
94+
private static function uniqueKeyPreserved(array $value): array
95+
{
96+
if (SupportArr::isAssoc($value)) {
97+
$entries = array_map(static fn($k, $v) => [$k, $v], array_keys($value), array_values($value));
98+
$entries = array_values(array_unique($entries, SORT_REGULAR));
99+
return array_combine(
100+
array_column($entries, 0),
101+
array_column($entries, 1)
102+
);
103+
}
104+
105+
return array_values(array_unique($value, SORT_REGULAR));
106+
}
107+
}

src/Support/With.php

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,22 @@
55
use Illuminate\Support\Arr;
66
use Illuminate\Support\Collection;
77

8+
/**
9+
* @deprecated Will be replaced by Arr::class.
10+
* @see \Ark4ne\JsonApi\Support\Arr
11+
*/
812
class With
913
{
1014
/**
11-
* @template T
12-
* @template R
15+
* @template TKey as array-key
16+
* @template TValue
17+
* @template UKey as array-key
18+
* @template UValue
1319
*
14-
* @param iterable<array-key, T> $base
15-
* @param iterable<array-key, R> $with
20+
* @param iterable<TKey, TValue> $base
21+
* @param iterable<UKey, UValue> $with
1622
*
17-
* @return array<array-key, T & R>
23+
* @return array<TKey & UKey, TValue & UValue>
1824
*/
1925
public static function merge(iterable $base, iterable $with): array
2026
{

tests/Unit/Resources/JsonApiResourceTest.php

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ public function testToCollectionPreserveKeys()
173173
{
174174
$resource = new class(null) extends JsonApiResource {
175175
public $preserveKeys = true;
176+
176177
public function toType(Request $request): string
177178
{
178179
return 'my-model';
@@ -185,4 +186,106 @@ public function toType(Request $request): string
185186
$this->assertEquals($resource::class, $collection->collects);
186187
$this->assertTrue(property_exists($collection, 'preserveKeys'));
187188
}
189+
190+
public function testReturnArray()
191+
{
192+
$resource = new class(null) extends JsonApiResource {
193+
public function toIdentifier(Request $request): string
194+
{
195+
return 1;
196+
}
197+
198+
public function toType(Request $request): string
199+
{
200+
return 'my-model';
201+
}
202+
203+
public function toAttributes(Request $request): iterable
204+
{
205+
$data = [
206+
'int' => 123,
207+
'str' => 'abc',
208+
];
209+
210+
return collect($data)->merge([
211+
'data' => collect($data)->merge([
212+
'data' => collect($data)
213+
])->all()
214+
]);
215+
}
216+
217+
public function toLinks(Request $request): iterable
218+
{
219+
$data = [
220+
'int' => 123,
221+
'str' => 'abc',
222+
];
223+
224+
return collect($data)->merge([
225+
'data' => collect($data)->merge([
226+
'data' => collect($data)
227+
])->all()
228+
]);
229+
}
230+
231+
public function toResourceMeta(Request $request): iterable
232+
{
233+
$data = [
234+
'int' => 123,
235+
'str' => 'abc',
236+
];
237+
238+
return collect($data)->merge([
239+
'data' => collect($data)->merge([
240+
'data' => collect($data)
241+
])->all()
242+
]);
243+
}
244+
};
245+
246+
$actual = $resource->toArray(new Request);
247+
248+
$expected = [
249+
'id' => 1,
250+
'type' => 'my-model',
251+
'attributes' => [
252+
'int' => 123,
253+
'str' => 'abc',
254+
'data' => [
255+
'int' => 123,
256+
'str' => 'abc',
257+
'data' => [
258+
'int' => 123,
259+
'str' => 'abc',
260+
]
261+
]
262+
],
263+
'links' => [
264+
'int' => 123,
265+
'str' => 'abc',
266+
'data' => [
267+
'int' => 123,
268+
'str' => 'abc',
269+
'data' => [
270+
'int' => 123,
271+
'str' => 'abc',
272+
]
273+
]
274+
],
275+
'meta' => [
276+
'int' => 123,
277+
'str' => 'abc',
278+
'data' => [
279+
'int' => 123,
280+
'str' => 'abc',
281+
'data' => [
282+
'int' => 123,
283+
'str' => 'abc',
284+
]
285+
]
286+
]
287+
];
288+
289+
$this->assertEquals($expected, $actual);
290+
}
188291
}

0 commit comments

Comments
 (0)