Skip to content

Commit 0221cc3

Browse files
committed
feat: implement applyWhen and deep merge MergeValue
1 parent 7312d60 commit 0221cc3

File tree

7 files changed

+192
-13
lines changed

7 files changed

+192
-13
lines changed

src/Resources/Concerns/Attributes.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@
22

33
namespace Ark4ne\JsonApi\Resources\Concerns;
44

5-
use Ark4ne\JsonApi\Descriptors\Resolver;
65
use Ark4ne\JsonApi\Support\Fields;
76
use Illuminate\Http\Request;
87

98
trait Attributes
109
{
11-
use Resolver;
10+
use PrepareData;
1211

1312
/**
1413
* @see https://jsonapi.org/format/#document-resource-object-attributes
@@ -42,7 +41,8 @@ protected function toAttributes(Request $request): iterable
4241
private function requestedAttributes(Request $request): array
4342
{
4443
return Fields::through($this->toType($request), function () use ($request) {
45-
$attributes = $this->resolveValues($request, $this->toAttributes($request));
44+
$attributes = $this->toAttributes($request);
45+
$attributes = $this->prepareData($request, $attributes);
4646
$attributes = $this->filter($attributes);
4747

4848
$fields = Fields::get($request);

src/Resources/Concerns/ConditionallyLoadsAttributes.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
use Ark4ne\JsonApi\Support\Fields;
66
use Ark4ne\JsonApi\Support\Includes;
77
use Illuminate\Http\Request;
8+
use Illuminate\Http\Resources\MergeValue;
9+
use Illuminate\Http\Resources\MissingValue;
810

911
trait ConditionallyLoadsAttributes
1012
{
@@ -39,4 +41,18 @@ protected function whenIncluded(Request $request, string $type, mixed $value)
3941
{
4042
return $this->when(Includes::include($request, $type), $value);
4143
}
44+
45+
/**
46+
* @param bool $condition
47+
* @param iterable<array-key, mixed> $data
48+
*
49+
* @return \Illuminate\Http\Resources\MergeValue
50+
*/
51+
protected function applyWhen(bool $condition, iterable $data): MergeValue
52+
{
53+
return new MergeValue($condition
54+
? $data
55+
: collect($data)->map(fn() => new MissingValue)
56+
);
57+
}
4258
}

src/Resources/Concerns/Links.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@
22

33
namespace Ark4ne\JsonApi\Resources\Concerns;
44

5-
use Ark4ne\JsonApi\Descriptors\Resolver;
65
use Illuminate\Http\Request;
76

87
trait Links
98
{
10-
use Resolver;
9+
use PrepareData;
1110

1211
/**
1312
* @see https://jsonapi.org/format/#document-resource-object-links
@@ -34,6 +33,6 @@ protected function toLinks(Request $request): ?iterable
3433
*/
3534
private function requestedLinks(Request $request): ?array
3635
{
37-
return $this->resolveValues($request, $this->toLinks($request));
36+
return $this->prepareData($request, $this->toLinks($request));
3837
}
3938
}

src/Resources/Concerns/Meta.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@
22

33
namespace Ark4ne\JsonApi\Resources\Concerns;
44

5-
use Ark4ne\JsonApi\Descriptors\Resolver;
65
use Illuminate\Http\Request;
76

87
trait Meta
98
{
10-
use Resolver;
9+
use PrepareData;
1110

1211
/**
1312
* @see https://jsonapi.org/format/#document-resource-objects
@@ -48,7 +47,7 @@ protected function toMeta(Request $request): ?iterable
4847
*/
4948
private function requestedResourceMeta(Request $request): ?array
5049
{
51-
return $this->resolveValues($request, $this->toResourceMeta($request));
50+
return $this->prepareData($request, $this->toResourceMeta($request));
5251
}
5352

5453
/**
@@ -58,6 +57,6 @@ private function requestedResourceMeta(Request $request): ?array
5857
*/
5958
private function requestedMeta(Request $request): ?array
6059
{
61-
return $this->resolveValues($request, $this->toMeta($request));
60+
return $this->prepareData($request, $this->toMeta($request));
6261
}
6362
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
namespace Ark4ne\JsonApi\Resources\Concerns;
4+
5+
use Ark4ne\JsonApi\Descriptors\Resolver;
6+
use Illuminate\Http\Request;
7+
use Illuminate\Http\Resources\MergeValue;
8+
use Illuminate\Support\Arr;
9+
10+
trait PrepareData
11+
{
12+
use Resolver;
13+
14+
/**
15+
* @template TKey extends array-key
16+
* @template TValue
17+
*
18+
* @param iterable<TKey, TValue> $data
19+
*
20+
* @return iterable<TKey, TValue>
21+
*/
22+
protected function prepareData(Request $request, ?iterable $data): iterable
23+
{
24+
return $data ? $this->resolveValues($request, $this->mergeValues($data)) : [];
25+
}
26+
27+
/**
28+
* @template TKey extends array-key
29+
* @template TValue
30+
*
31+
* @param iterable<TKey, TValue> $data
32+
*
33+
* @return array<TKey, TValue>
34+
*/
35+
protected function mergeValues(iterable $data): array
36+
{
37+
$data = collect($data)->all();
38+
39+
$index = -1;
40+
41+
foreach ($data as $key => $value) {
42+
$index++;
43+
44+
if (is_iterable($value)) {
45+
$data[$key] = $this->mergeValues($value);
46+
47+
continue;
48+
}
49+
50+
if (is_numeric($key) && $value instanceof MergeValue) {
51+
if (Arr::isList($value->data)) {
52+
return array_merge(
53+
array_merge(array_slice($data, 0, $index, true), $this->mergeValues($value->data)),
54+
$this->mergeValues(array_values(array_slice($data, $index + 1, null, true)))
55+
);
56+
}
57+
58+
return array_slice($data, 0, $index, true) +
59+
$this->mergeValues($value->data) +
60+
$this->mergeValues(array_slice($data, $index + 1, null, true));
61+
}
62+
}
63+
64+
return $data;
65+
}
66+
}

tests/Unit/Resources/Concerns/ConditionallyLoadsAttributesTest.php

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@
55
use Ark4ne\JsonApi\Resources\Concerns\ConditionallyLoadsAttributes;
66
use Illuminate\Http\Request;
77
use Illuminate\Http\Resources\Json\JsonResource;
8+
use Illuminate\Http\Resources\MergeValue;
89
use Illuminate\Http\Resources\MissingValue;
910
use Test\Support\Reflect;
1011
use Test\TestCase;
1112

1213
class ConditionallyLoadsAttributesTest extends TestCase
1314
{
14-
public function dataWhenInclude()
15+
public function data()
1516
{
1617
return [
1718
[false, 'test', []],
@@ -20,7 +21,7 @@ public function dataWhenInclude()
2021
}
2122

2223
/**
23-
* @dataProvider dataWhenInclude
24+
* @dataProvider data
2425
*/
2526
public function testWhenInclude($expected, $property, $query)
2627
{
@@ -37,7 +38,7 @@ public function testWhenInclude($expected, $property, $query)
3738
}
3839

3940
/**
40-
* @dataProvider dataWhenInclude
41+
* @dataProvider data
4142
*/
4243
public function testWhenInFields($expected, $property, $query)
4344
{
@@ -57,4 +58,27 @@ protected function toType()
5758
Reflect::invoke($stub, 'whenInFields', $request, $property, true)
5859
);
5960
}
61+
62+
public function testApplyWhen()
63+
{
64+
$stub = new class(null) extends JsonResource {
65+
use ConditionallyLoadsAttributes;
66+
};
67+
$actual = Reflect::invoke($stub, 'applyWhen', false, [
68+
'missing.1' => 'abc',
69+
'missing.2' => 123,
70+
]);
71+
$this->assertEquals(new MergeValue([
72+
'missing.1' => new MissingValue,
73+
'missing.2' => new MissingValue,
74+
]), $actual);
75+
$actual = Reflect::invoke($stub, 'applyWhen', true, [
76+
'present.1' => 'abc',
77+
'present.2' => 123,
78+
]);
79+
$this->assertEquals(new MergeValue([
80+
'present.1' => 'abc',
81+
'present.2' => 123,
82+
]), $actual);
83+
}
6084
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php
2+
3+
namespace Test\Unit\Resources\Concerns;
4+
5+
use Ark4ne\JsonApi\Resources\Concerns\PrepareData;
6+
use Illuminate\Http\Resources\MergeValue;
7+
use Illuminate\Http\Resources\MissingValue;
8+
use Test\Support\Reflect;
9+
use Test\TestCase;
10+
11+
class PrepareDataTest extends TestCase
12+
{
13+
public function testMergeValues()
14+
{
15+
$stub = new class {
16+
use PrepareData;
17+
};
18+
19+
$actual = Reflect::invoke($stub, 'mergeValues', []);
20+
$this->assertEquals([], $actual);
21+
$actual = Reflect::invoke($stub, 'mergeValues', collect());
22+
$this->assertEquals([], $actual);
23+
24+
$expected = $sample = [
25+
'a' => 'abc',
26+
'b' => 123,
27+
'c' => true,
28+
'd' => false,
29+
'e' => null,
30+
'f' => new MissingValue,
31+
];
32+
$actual = Reflect::invoke($stub, 'mergeValues', $sample);
33+
$this->assertEquals($expected, $actual);
34+
$actual = Reflect::invoke($stub, 'mergeValues', collect($sample));
35+
$this->assertEquals($expected, $actual);
36+
37+
$sampleWithMerge = $sample;
38+
$sampleWithMerge[] = new MergeValue($sample);
39+
40+
$actual = Reflect::invoke($stub, 'mergeValues', $sampleWithMerge);
41+
$this->assertEquals($expected, $actual);
42+
43+
$actual = Reflect::invoke($stub, 'mergeValues', $sample + [
44+
new MergeValue([
45+
'g' => '12345',
46+
'h' => [
47+
'i' => 'def',
48+
new MergeValue([new MergeValue([true])]),
49+
new MergeValue([[new MergeValue([[true]])]])
50+
],
51+
'k' => [
52+
new MergeValue([1, 2, 3]),
53+
new MergeValue([4, 5, 6])
54+
]
55+
])
56+
]
57+
);
58+
$this->assertEquals($sample + [
59+
'g' => '12345',
60+
'h' => [
61+
'i' => 'def',
62+
true,
63+
[[true]],
64+
],
65+
'k' => [
66+
1,
67+
2,
68+
3,
69+
4,
70+
5,
71+
6
72+
]
73+
], $actual);
74+
}
75+
}

0 commit comments

Comments
 (0)