Skip to content

Commit a6437be

Browse files
committed
feat: implement whenHas & unless methods, add config for auto check has attributes
1 parent d692e1e commit a6437be

File tree

12 files changed

+203
-18
lines changed

12 files changed

+203
-18
lines changed

config/jsonapi.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,20 @@
4242
'precision' => null,
4343
],
4444

45+
'attribute' => [
46+
/*
47+
|--------------------------------------------------------------------------
48+
| When Has
49+
|--------------------------------------------------------------------------
50+
|
51+
| @see whenHas()
52+
|
53+
| Apply automatically whenHas condition on attributes.
54+
|
55+
*/
56+
'when-has' => false,
57+
],
58+
4559
'relationship' => [
4660
/*
4761
|--------------------------------------------------------------------------

src/Descriptors/Describer.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,24 @@ public function when(bool|Closure $condition): static
3535
return $this;
3636
}
3737

38+
/**
39+
* Display field whether the given condition is true.
40+
*
41+
* @param bool|Closure(Request, T, string):bool $condition
42+
*
43+
* @return static
44+
*/
45+
public function unless(bool|Closure $condition): static
46+
{
47+
$this->rules[] = static fn(
48+
Request $request,
49+
mixed $model,
50+
string $attribute
51+
): bool => !value($condition, $request, $model, $attribute);
52+
53+
return $this;
54+
}
55+
3856
/**
3957
* Display field whether the accessor field is not null.
4058
*

src/Descriptors/Relations/Relation.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public function __construct(
3232
protected string $related,
3333
protected null|string|Closure $relation
3434
) {
35-
$this->whenIncluded = Config::$whenIncluded;
35+
$this->whenIncluded = Config::$autoWhenIncluded;
3636
}
3737

3838
/**

src/Descriptors/Values/Value.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,25 @@ public function whenInFields(): static
6969
): bool => Fields::has($request, $attribute));
7070
}
7171

72+
/**
73+
* Display an attribute if it exists on the resource.
74+
*
75+
* @param string|null $field
76+
* @return static
77+
*/
78+
public function whenHas(string $field = null): static
79+
{
80+
return $this->when(static fn(
81+
Request $request,
82+
mixed $model,
83+
string $attribute
84+
): bool => match (true) {
85+
$model instanceof Model => array_key_exists($field ?? $attribute, $model->getAttributes()),
86+
IlluminateArr::accessible($model) => array_key_exists($field ?? $attribute, $model),
87+
default => property_exists($model, $field ?? $attribute)
88+
});
89+
}
90+
7291
public function resolveFor(Request $request, mixed $model, string $field): mixed
7392
{
7493
if ($this->attribute instanceof Closure) {

src/Resources/Concerns/Attributes.php

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@ protected function toAttributes(Request $request): iterable
5151
private function requestedAttributes(Request $request): array
5252
{
5353
return Fields::through($this->toType($request), function () use ($request) {
54-
$attributes = $this->toAttributes($request);
55-
$attributes = $this->prepareData($request, $attributes);
54+
$attributes = $this->resolveAttributes($request);
5655
$attributes = $this->filter($attributes);
5756

5857
$fields = Fields::get($request);
@@ -64,4 +63,19 @@ private function requestedAttributes(Request $request): array
6463
return array_map('\value', $attributes);
6564
});
6665
}
66+
67+
private function prepareAttributes(Request $request)
68+
{
69+
$attributes = $this->toAttributes($request);
70+
$attributes = $this->mergeValues($attributes);
71+
72+
return $this->autoWhenHas($attributes);
73+
}
74+
75+
private function resolveAttributes(Request $request)
76+
{
77+
$attributes = $this->prepareAttributes($request);
78+
79+
return $this->resolveValues($request, $attributes);
80+
}
6781
}

src/Resources/Concerns/ConditionallyLoadsAttributes.php

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Ark4ne\JsonApi\Resources\Relationship;
88
use Ark4ne\JsonApi\Support\Fields;
99
use Ark4ne\JsonApi\Support\Includes;
10+
use Ark4ne\JsonApi\Support\Supported;
1011
use Illuminate\Http\Request;
1112
use Illuminate\Http\Resources\MergeValue;
1213
use Illuminate\Http\Resources\MissingValue;
@@ -19,8 +20,8 @@ trait ConditionallyLoadsAttributes
1920
* @template K
2021
*
2122
* @param \Illuminate\Http\Request $request
22-
* @param string $attribute
23-
* @param K $value
23+
* @param string $attribute
24+
* @param K $value
2425
*
2526
* @return \Illuminate\Http\Resources\MissingValue|K
2627
*/
@@ -35,8 +36,8 @@ protected function whenInFields(Request $request, string $attribute, mixed $valu
3536
* @template K
3637
*
3738
* @param \Illuminate\Http\Request $request
38-
* @param string $type
39-
* @param K $value
39+
* @param string $type
40+
* @param K $value
4041
*
4142
* @return \Illuminate\Http\Resources\MissingValue|K
4243
*/
@@ -46,7 +47,7 @@ protected function whenIncluded(Request $request, string $type, mixed $value)
4647
}
4748

4849
/**
49-
* @param bool $condition
50+
* @param bool $condition
5051
* @param iterable<array-key, mixed> $data
5152
*
5253
* @return \Illuminate\Http\Resources\MergeValue
@@ -69,4 +70,56 @@ protected function applyWhen(bool $condition, iterable $data): MergeValue
6970
return new MissingValue();
7071
}));
7172
}
73+
74+
/**
75+
* @polyfill JsonResource::whenHas
76+
* @see https://github.com/laravel/framework/pull/45376/files
77+
*
78+
* Retrieve an attribute if it exists on the resource.
79+
*
80+
* @param string $attribute
81+
* @param mixed $value
82+
* @param mixed $default
83+
* @return \Illuminate\Http\Resources\MissingValue|mixed
84+
*/
85+
public function whenHas($attribute, $value = null, $default = null)
86+
{
87+
if (Supported::$whenHas) {
88+
return parent::whenHas($attribute, $value, $default);
89+
}
90+
91+
if (func_num_args() < 3) {
92+
$default = new MissingValue;
93+
}
94+
95+
if (!array_key_exists($attribute, $this->resource->getAttributes())) {
96+
return value($default);
97+
}
98+
99+
return func_num_args() === 1
100+
? $this->resource->{$attribute}
101+
: value($value, $this->resource->{$attribute});
102+
}
103+
104+
/**
105+
* @polyfill JsonResource::unless
106+
* @see https://github.com/laravel/framework/pull/45419/files
107+
*
108+
* Retrieve a value if the given "condition" is falsy.
109+
*
110+
* @param bool $condition
111+
* @param mixed $value
112+
* @param mixed $default
113+
* @return \Illuminate\Http\Resources\MissingValue|mixed
114+
*/
115+
public function unless($condition, $value, $default = null)
116+
{
117+
if (Supported::$whenHas) {
118+
return parent::unless($condition, $value, $default);
119+
}
120+
121+
$arguments = func_num_args() === 2 ? [$value] : [$value, $default];
122+
123+
return $this->when(! $condition, ...$arguments);
124+
}
72125
}

src/Resources/Concerns/Links.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ protected function toLinks(Request $request): ?iterable
3333
*/
3434
private function requestedLinks(Request $request): array
3535
{
36-
return $this->prepareData($request, $this->toLinks($request));
36+
$links = $this->toLinks($request) ?? [];
37+
$links = $this->mergeValues($links);
38+
39+
return $this->resolveValues($request, $links);
3740
}
3841
}

src/Resources/Concerns/Meta.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@ protected function toMeta(Request $request): ?iterable
4747
*/
4848
private function requestedResourceMeta(Request $request): array
4949
{
50-
return $this->prepareData($request, $this->toResourceMeta($request));
50+
$meta = $this->toResourceMeta($request) ?? [];
51+
$meta = $this->mergeValues($meta);
52+
53+
return $this->resolveValues($request, $meta);
5154
}
5255

5356
/**
@@ -57,6 +60,9 @@ private function requestedResourceMeta(Request $request): array
5760
*/
5861
private function requestedMeta(Request $request): array
5962
{
60-
return $this->prepareData($request, $this->toMeta($request));
63+
$meta = $this->toMeta($request) ?? [];
64+
$meta = $this->mergeValues($meta);
65+
66+
return $this->resolveValues($request, $meta);
6167
}
6268
}

src/Resources/Concerns/PrepareData.php

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@
33
namespace Ark4ne\JsonApi\Resources\Concerns;
44

55
use Ark4ne\JsonApi\Descriptors\Resolver;
6+
use Ark4ne\JsonApi\Descriptors\Values\Value;
7+
use Ark4ne\JsonApi\Support\Config;
68
use Ark4ne\JsonApi\Support\Values;
79
use Illuminate\Http\Request;
8-
use Illuminate\Http\Resources\Json\JsonResource;
9-
use Illuminate\Http\Resources\MergeValue;
10-
use Illuminate\Http\Resources\PotentiallyMissing;
11-
use Illuminate\Support\Arr;
10+
use Illuminate\Support\Collection;
1211

1312
trait PrepareData
1413
{
@@ -17,6 +16,41 @@ trait PrepareData
1716
/**
1817
* @template TKey as array-key
1918
* @template TValue
19+
20+
* @param iterable<TKey, TValue> $data
21+
*
22+
* @return iterable<TKey, TValue>
23+
*/
24+
protected function mergeValues(iterable $data) : iterable {
25+
26+
return Values::mergeValues($data);
27+
}
28+
29+
/**
30+
* @template TKey as array-key
31+
* @template TValue
32+
33+
* @param iterable<TKey, TValue> $data
34+
*
35+
* @return iterable<TKey, TValue>
36+
*/
37+
protected function autoWhenHas(iterable $data): iterable
38+
{
39+
if (!Config::$autoWhenHas) {
40+
return $data;
41+
}
42+
43+
return (new Collection($data))
44+
->map(fn($value, int|string $key) => $value instanceof Value
45+
? $value->whenHas()
46+
: $this->whenHas($key, $value));
47+
}
48+
49+
/**
50+
* @deprecated
51+
*
52+
* @template TKey as array-key
53+
* @template TValue
2054
*
2155
* @param \Illuminate\Http\Request $request
2256
* @param iterable<TKey, TValue>|null $data
@@ -25,6 +59,10 @@ trait PrepareData
2559
*/
2660
protected function prepareData(Request $request, ?iterable $data): iterable
2761
{
62+
if ($data === null) {
63+
return [];
64+
}
65+
2866
return $data ? $this->resolveValues($request, Values::mergeValues($data)) : [];
2967
}
3068
}

src/Resources/Relationship.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public function __construct(
3838
protected ?Closure $links = null,
3939
protected ?Closure $meta = null
4040
) {
41-
$this->whenIncluded = Config::$whenIncluded;
41+
$this->whenIncluded = Config::$autoWhenIncluded;
4242
}
4343

4444
/**

0 commit comments

Comments
 (0)