Skip to content

Commit fa9b25a

Browse files
committed
wip: auto whenHas
1 parent d15b1c3 commit fa9b25a

File tree

10 files changed

+130
-37
lines changed

10 files changed

+130
-37
lines changed

config/jsonapi.php

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,8 @@
4040
|
4141
*/
4242
'precision' => null,
43-
],
4443

45-
'attribute' => [
44+
'when-has' => false,
4645
/*
4746
|--------------------------------------------------------------------------
4847
| When Has
@@ -52,8 +51,23 @@
5251
|
5352
| Apply automatically whenHas condition on attributes.
5453
|
54+
| false => disable auto-when-has
55+
| true => enable for all scopes
56+
| [] => specify scopes : ['attributes' | 'resource-meta' | 'meta']
57+
|
58+
| AutoWhenHas can be applied only on none closure value :
59+
| - Applied :
60+
| 'name' => $this->string(), // apply whenHas('name')
61+
| 'name' => $this->string('first_name'), // apply whenHas('first_name')
62+
| $this->string('first_name'), // apply whenHas('first_name')
63+
|
64+
| - Not applied :
65+
| 'name' => $this->string(fn() => $this->name . ' ' . $this->first_name),
66+
| Insteadof you should specify yourself whenHas :
67+
| 'name' => $this->string(fn() => $this->last_name . ' ' . $this->first_name)
68+
| ->whenHas('last_name')
69+
| ->whenHas('first_name'),
5570
*/
56-
'when-has' => false,
5771
],
5872

5973
'relationship' => [

src/Descriptors/Relations/Relation.php

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use Ark4ne\JsonApi\Descriptors\Describer;
66
use Ark4ne\JsonApi\Resources\Relationship;
77
use Ark4ne\JsonApi\Traits\HasRelationLoad;
8-
use Ark4ne\JsonApi\Support\Config;
98
use Closure;
109
use Illuminate\Database\Eloquent\Model;
1110
use Illuminate\Http\Request;
@@ -22,7 +21,7 @@ abstract class Relation extends Describer
2221

2322
protected ?Closure $links = null;
2423
protected ?Closure $meta = null;
25-
protected bool $whenIncluded;
24+
protected ?bool $whenIncluded = null;
2625

2726
/**
2827
* @param class-string<\Ark4ne\JsonApi\Resources\JsonApiResource|\Ark4ne\JsonApi\Resources\JsonApiCollection> $related
@@ -32,7 +31,6 @@ public function __construct(
3231
protected string $related,
3332
protected null|string|Closure $relation
3433
) {
35-
$this->whenIncluded = Config::$autoWhenIncluded;
3634
}
3735

3836
/**
@@ -63,9 +61,18 @@ public function meta(Closure $meta): static
6361
return $this;
6462
}
6563

66-
public function whenIncluded(): static
64+
/**
65+
* @param bool|null $whenIncluded
66+
* @return $this
67+
*/
68+
public function whenIncluded(bool $whenIncluded = null): static
6769
{
68-
$this->whenIncluded = true;
70+
if ($whenIncluded === null) {
71+
$this->whenIncluded ??= true;
72+
} else {
73+
$this->whenIncluded = $whenIncluded;
74+
}
75+
6976
return $this;
7077
}
7178

@@ -123,8 +130,8 @@ public function resolveFor(Request $request, mixed $model, string $field): Relat
123130

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

126-
if ($this->whenIncluded) {
127-
$relation->whenIncluded();
133+
if ($this->whenIncluded !== null) {
134+
$relation->whenIncluded($this->whenIncluded);
128135
}
129136

130137
return $relation;

src/Descriptors/Values/Value.php

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
*/
1717
abstract class Value extends Describer
1818
{
19+
protected ?bool $autoWhenHas = null;
20+
1921
protected bool $nullable;
2022

2123
public function __construct(
@@ -40,6 +42,19 @@ public function isNullable(): bool
4042
return $this->nullable;
4143
}
4244

45+
/**
46+
* Add or reset auto check when has attribute.
47+
*
48+
* @param bool $autoWhenHas
49+
* @return static
50+
*/
51+
public function autoWhenHas(bool $autoWhenHas = true): static
52+
{
53+
$this->autoWhenHas = $autoWhenHas;
54+
55+
return $this;
56+
}
57+
4358
/**
4459
* Display attribute whether the accessor attribute has been appended.
4560
*
@@ -74,12 +89,12 @@ public function whenInFields(): static
7489
* @param string|null $field
7590
* @return static
7691
*/
77-
public function whenHas(string $field = null): static
92+
public function whenHas(?string $field = null): static
7893
{
7994
return $this->when(static fn(
8095
Request $request,
8196
mixed $model,
82-
string $attribute
97+
string $attribute
8398
): bool => Values::hasAttribute($model, $field ?? $attribute));
8499
}
85100

@@ -96,5 +111,14 @@ public function resolveFor(Request $request, mixed $model, string $field): mixed
96111
: $this->value($value);
97112
}
98113

114+
protected function check(Request $request, mixed $model, string $attribute): bool
115+
{
116+
if (($this->autoWhenHas ?? false) && !($this->attribute instanceof Closure)) {
117+
$this->whenHas($this->attribute);
118+
}
119+
120+
return parent::check($request, $model, $attribute);
121+
}
122+
99123
abstract protected function value(mixed $of): mixed;
100124
}

src/Resources/Concerns/Attributes.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ private function prepareAttributes(Request $request): iterable
7373
$attributes = $this->toAttributes($request);
7474
$attributes = $this->mergeValues($attributes);
7575

76-
return $this->autoWhenHas($attributes);
76+
return $this->autoWhenHas($attributes, 'attributes');
7777
}
7878

7979
/**

src/Resources/Concerns/Meta.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ private function requestedResourceMeta(Request $request): array
4949
{
5050
$meta = $this->toResourceMeta($request) ?? [];
5151
$meta = $this->mergeValues($meta);
52+
$meta = $this->autoWhenHas($meta, 'resource-meta');
5253

5354
return $this->resolveValues($request, $meta);
5455
}
@@ -62,6 +63,7 @@ private function requestedMeta(Request $request): array
6263
{
6364
$meta = $this->toMeta($request) ?? [];
6465
$meta = $this->mergeValues($meta);
66+
$meta = $this->autoWhenHas($meta, 'meta');
6567

6668
return $this->resolveValues($request, $meta);
6769
}

src/Resources/Concerns/PrepareData.php

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
namespace Ark4ne\JsonApi\Resources\Concerns;
44

5+
use Ark4ne\JsonApi\Descriptors\Relations\Relation;
56
use Ark4ne\JsonApi\Descriptors\Resolver;
67
use Ark4ne\JsonApi\Descriptors\Values\Value;
8+
use Ark4ne\JsonApi\Resources\Relationship;
79
use Ark4ne\JsonApi\Support\Config;
810
use Ark4ne\JsonApi\Support\Values;
911
use Illuminate\Http\Request;
@@ -16,46 +18,61 @@ trait PrepareData
1618
/**
1719
* @template TKey as array-key
1820
* @template TValue
19-
2021
* @param iterable<TKey, TValue> $data
2122
*
2223
* @return iterable<TKey, TValue>
2324
*/
24-
protected function mergeValues(iterable $data) : iterable {
25+
protected function mergeValues(iterable $data): iterable
26+
{
2527

2628
return Values::mergeValues($data);
2729
}
2830

2931
/**
3032
* @template TKey as array-key
3133
* @template TValue
32-
3334
* @param iterable<TKey, TValue> $data
3435
*
3536
* @return iterable<TKey, TValue>
3637
*/
37-
protected function autoWhenHas(iterable $data): iterable
38+
protected function autoWhenHas(iterable $data, string $for): iterable
39+
{
40+
$autoWhenHas = $this->autoWhenHas ?? null;
41+
42+
if ($autoWhenHas || (Config::autoWhenHas($for) && $autoWhenHas !== false)) {
43+
(new Collection($data))
44+
->each(fn($value, int|string $key) => $value instanceof Value
45+
? $value->autoWhenHas()
46+
: $value);
47+
}
48+
49+
return $data;
50+
}
51+
52+
protected function autoWhenIncluded(iterable $data): iterable
3853
{
39-
if (!Config::$autoWhenHas) {
40-
return $data;
54+
$autoWhenIncluded = $this->autoWhenIncluded ?? null;
55+
56+
if ($autoWhenIncluded || (Config::$autoWhenIncluded && $autoWhenIncluded !== false)) {
57+
(new Collection($data))
58+
->each(fn($relation, int|string $key) => $relation instanceof Relation || $relation instanceof Relationship
59+
? $relation->whenIncluded()
60+
: $relation);
4161
}
4262

43-
return (new Collection($data))
44-
->map(fn($value, int|string $key) => $value instanceof Value
45-
? $value->whenHas()
46-
: $this->whenHas($key, $value));
63+
return $data;
4764
}
4865

4966
/**
67+
* @param \Illuminate\Http\Request $request
68+
* @param iterable<TKey, TValue>|null $data
69+
*
70+
* @return iterable<TKey, TValue>
5071
* @deprecated
5172
*
5273
* @template TKey as array-key
5374
* @template TValue
5475
*
55-
* @param \Illuminate\Http\Request $request
56-
* @param iterable<TKey, TValue>|null $data
57-
*
58-
* @return iterable<TKey, TValue>
5976
*/
6077
protected function prepareData(Request $request, ?iterable $data): iterable
6178
{

src/Resources/Concerns/Relationships.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,8 @@ private function requestedRelationships(Request $request): array
103103
{
104104
$relations = [];
105105
$relationships = $this->toRelationships($request);
106-
$relationships = Values::mergeValues($relationships);
106+
$relationships = $this->mergeValues($relationships);
107+
$relationships = $this->autoWhenIncluded($relationships);
107108
$relationships = $this->resolveValues($request, $relationships);
108109
$relationships = $this->filter($relationships);
109110

src/Resources/Relationship.php

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

33
namespace Ark4ne\JsonApi\Resources;
44

5-
use Ark4ne\JsonApi\Support\Config;
65
use Ark4ne\JsonApi\Support\Values;
76
use Ark4ne\JsonApi\Traits\HasRelationLoad;
87
use Closure;
@@ -24,7 +23,7 @@ class Relationship implements Resourceable
2423

2524
protected bool $asCollection = false;
2625

27-
protected bool $whenIncluded;
26+
protected ?bool $whenIncluded = null;
2827

2928
/**
3029
* @param class-string<T> $resource
@@ -38,7 +37,6 @@ public function __construct(
3837
protected ?Closure $links = null,
3938
protected ?Closure $meta = null
4039
) {
41-
$this->whenIncluded = Config::$autoWhenIncluded;
4240
}
4341

4442
/**
@@ -99,11 +97,16 @@ public function asCollection(): self
9997
/**
10098
* Only load value if relation is included
10199
*
100+
* @param bool|null $whenIncluded
102101
* @return $this
103102
*/
104-
public function whenIncluded(): self
103+
public function whenIncluded(bool $whenIncluded = null): static
105104
{
106-
$this->whenIncluded = true;
105+
if ($whenIncluded === null) {
106+
$this->whenIncluded ??= true;
107+
} else {
108+
$this->whenIncluded = $whenIncluded;
109+
}
107110

108111
return $this;
109112
}

src/Support/Config.php

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ class Config
1212

1313
public static bool $autoWhenIncluded = false;
1414

15-
public static bool $autoWhenHas = false;
15+
/** @var array<string, bool>|bool */
16+
public static array|bool $autoWhenHas = false;
1617

1718
public static int|null $precision = null;
1819

@@ -21,7 +22,23 @@ public static function boot(): void
2122
self::$nullable = config('jsonapi.describer.nullable', self::$nullable);
2223
self::$date = config('jsonapi.describer.date', self::$date);
2324
self::$precision = config('jsonapi.describer.precision', self::$precision);
24-
self::$autoWhenHas = config('jsonapi.attribute.when-has', self::$autoWhenHas);
25+
self::$autoWhenHas = config('jsonapi.describer.when-has', self::$autoWhenHas);
2526
self::$autoWhenIncluded = config('jsonapi.relationship.when-included', self::$autoWhenIncluded);
27+
28+
if (is_array(self::$autoWhenHas)) {
29+
self::$autoWhenHas = array_fill_keys(self::$autoWhenHas, true);
30+
}
31+
}
32+
33+
public static function autoWhenHas(string $type): bool
34+
{
35+
if (self::$autoWhenHas === false) {
36+
return false;
37+
}
38+
if (self::$autoWhenHas === true) {
39+
return true;
40+
}
41+
42+
return self::$autoWhenHas[$type] ?? false;
2643
}
2744
}

tests/Unit/Descriptors/RelationTest.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,21 @@ public function testIncluded($model)
3232
$stub = new RelationOne(UserResource::class, fn() => null);
3333

3434
$relation = $stub->resolveFor(new Request, $model, 'null');
35-
3635
$this->assertInstanceOf(Relationship::class, $relation);
37-
$this->assertFalse(Reflect::get($relation, 'whenIncluded'));
36+
$this->assertNull(Reflect::get($relation, 'whenIncluded'));
3837

3938
$stub->whenIncluded();
4039
$relation = $stub->resolveFor(new Request, $model, 'null');
40+
$this->assertInstanceOf(Relationship::class, $relation);
41+
$this->assertTrue(Reflect::get($relation, 'whenIncluded'));
4142

43+
$stub->whenIncluded(false);
44+
$relation = $stub->resolveFor(new Request, $model, 'null');
45+
$this->assertInstanceOf(Relationship::class, $relation);
46+
$this->assertFalse(Reflect::get($relation, 'whenIncluded'));
47+
48+
$stub->whenIncluded(true);
49+
$relation = $stub->resolveFor(new Request, $model, 'null');
4250
$this->assertInstanceOf(Relationship::class, $relation);
4351
$this->assertTrue(Reflect::get($relation, 'whenIncluded'));
4452
}

0 commit comments

Comments
 (0)