Skip to content

Commit 74e5aef

Browse files
committed
feat: implement descriptable values (attributes & relations)
1 parent 1f080b5 commit 74e5aef

33 files changed

+1340
-50
lines changed

src/Descriptors/Descriptors.php

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
<?php
2+
3+
namespace Ark4ne\JsonApi\Descriptors;
4+
5+
use Ark4ne\JsonApi\Descriptors\Relations\RelationMany;
6+
use Ark4ne\JsonApi\Descriptors\Relations\RelationOne;
7+
use Ark4ne\JsonApi\Descriptors\Values\{
8+
ValueArray,
9+
ValueBool,
10+
ValueDate,
11+
ValueFloat,
12+
ValueInteger,
13+
ValueMixed,
14+
ValueString
15+
};
16+
use Closure;
17+
18+
/**
19+
* @template T as \Illuminate\Database\Eloquent\Model
20+
*/
21+
trait Descriptors
22+
{
23+
/**
24+
* @param null|string|Closure(T):bool $attribute
25+
*
26+
* @return \Ark4ne\JsonApi\Descriptors\Values\ValueBool<T>
27+
*/
28+
protected function bool(null|string|Closure $attribute = null): ValueBool
29+
{
30+
return new ValueBool($attribute);
31+
}
32+
33+
/**
34+
* @param null|string|Closure(T):int $attribute
35+
*
36+
* @return \Ark4ne\JsonApi\Descriptors\Values\ValueInteger<T>
37+
*/
38+
protected function integer(null|string|Closure $attribute = null): ValueInteger
39+
{
40+
return new ValueInteger($attribute);
41+
}
42+
43+
/**
44+
* @param null|string|Closure(T):float $attribute
45+
*
46+
* @return \Ark4ne\JsonApi\Descriptors\Values\ValueFloat<T>
47+
*/
48+
public function float(null|string|Closure $attribute = null): ValueFloat
49+
{
50+
return new ValueFloat($attribute);
51+
}
52+
53+
/**
54+
* @param null|string|Closure(T):string $attribute
55+
*
56+
* @return \Ark4ne\JsonApi\Descriptors\Values\ValueString<T>
57+
*/
58+
protected function string(null|string|Closure $attribute = null): ValueString
59+
{
60+
return new ValueString($attribute);
61+
}
62+
63+
/**
64+
* @param null|string|Closure(T):\DateTimeInterface $attribute
65+
*
66+
* @return \Ark4ne\JsonApi\Descriptors\Values\ValueDate<T>
67+
*/
68+
protected function date(null|string|Closure $attribute = null): ValueDate
69+
{
70+
return new ValueDate($attribute);
71+
}
72+
73+
/**
74+
* @param null|string|Closure(T):array $attribute
75+
*
76+
* @return \Ark4ne\JsonApi\Descriptors\Values\ValueArray<T>
77+
*/
78+
protected function array(null|string|Closure $attribute = null): ValueArray
79+
{
80+
return new ValueArray($attribute);
81+
}
82+
83+
/**
84+
* @param null|string|Closure(T):object $attribute
85+
*
86+
* @return \Ark4ne\JsonApi\Descriptors\Values\ValueMixed<T>
87+
*/
88+
protected function mixed(null|string|Closure $attribute = null): ValueMixed
89+
{
90+
return new ValueMixed($attribute);
91+
}
92+
93+
/**
94+
* @param class-string<\Ark4ne\JsonApi\Resources\JsonApiResource|\Ark4ne\JsonApi\Resources\JsonApiCollection> $for
95+
* @param null|string|Closure(T):mixed $relation
96+
*
97+
* @return \Ark4ne\JsonApi\Descriptors\Relations\RelationOne<T>
98+
*/
99+
protected function one(string $for, null|string|Closure $relation = null): RelationOne
100+
{
101+
return new RelationOne($for, $relation);
102+
}
103+
104+
/**
105+
* @param class-string<\Ark4ne\JsonApi\Resources\JsonApiResource|\Ark4ne\JsonApi\Resources\JsonApiCollection> $for
106+
* @param null|string|Closure(T):mixed $relation
107+
*
108+
* @return \Ark4ne\JsonApi\Descriptors\Relations\RelationMany<T>
109+
*/
110+
protected function many(string $for, null|string|Closure $relation = null): RelationMany
111+
{
112+
return new RelationMany($for, $relation);
113+
}
114+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<?php
2+
3+
namespace Ark4ne\JsonApi\Descriptors\Relations;
4+
5+
use Ark4ne\JsonApi\Descriptors\Valuable;
6+
use Ark4ne\JsonApi\Resources\Relationship;
7+
use Closure;
8+
use Illuminate\Database\Eloquent\Model;
9+
use Illuminate\Http\Request;
10+
11+
abstract class Relation extends Valuable
12+
{
13+
protected ?Closure $links = null;
14+
protected ?Closure $meta = null;
15+
protected bool $whenIncluded = false;
16+
17+
/**
18+
* @param class-string<\Ark4ne\JsonApi\Resources\JsonApiResource|\Ark4ne\JsonApi\Resources\JsonApiCollection> $related
19+
* @param string|\Closure|null $relation
20+
*/
21+
public function __construct(
22+
protected string $related,
23+
protected null|string|Closure $relation
24+
) {
25+
}
26+
27+
/**
28+
* @return class-string<\Ark4ne\JsonApi\Resources\JsonApiResource|\Ark4ne\JsonApi\Resources\JsonApiCollection>
29+
*/
30+
public function related(): string
31+
{
32+
return $this->related;
33+
}
34+
35+
/**
36+
* @return null|string|Closure
37+
*/
38+
public function retriever(): null|string|Closure
39+
{
40+
return $this->relation;
41+
}
42+
43+
public function links(Closure $links): static
44+
{
45+
$this->links = $links;
46+
return $this;
47+
}
48+
49+
public function meta(Closure $meta): static
50+
{
51+
$this->links = $meta;
52+
return $this;
53+
}
54+
55+
public function whenIncluded(): static
56+
{
57+
$this->whenIncluded = true;
58+
return $this;
59+
}
60+
61+
public function whenLoaded(string $relation = null): static
62+
{
63+
return $this->when(fn(
64+
Request $request,
65+
Model $model,
66+
string $attribute
67+
): bool => $model->relationLoaded($relation ?? $this->relation ?? $attribute));
68+
}
69+
70+
public function whenPivotLoaded(string $table, string $accessor = null): static
71+
{
72+
return $this->when(fn(
73+
Request $request,
74+
Model $model,
75+
string $attribute
76+
): bool => ($pivot = $model->{$accessor ?? $this->relation ?? $attribute})
77+
&& (
78+
$pivot instanceof $table ||
79+
$pivot->getTable() === $table
80+
)
81+
);
82+
}
83+
84+
public function resolveFor(Request $request, Model $model, string $field): Relationship
85+
{
86+
if ($this->relation instanceof Closure) {
87+
$value = fn() => ($this->relation)($model, $field);
88+
} else {
89+
$value = fn() => $model->getRelationValue($this->relation ?? $field);
90+
}
91+
92+
$relation = $this->value($value);
93+
94+
if ($this->whenIncluded) {
95+
$relation->whenIncluded();
96+
}
97+
98+
return $relation;
99+
}
100+
101+
abstract protected function value(Closure $value): Relationship;
102+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Ark4ne\JsonApi\Descriptors\Relations;
4+
5+
use Ark4ne\JsonApi\Resources\Relationship;
6+
use Closure;
7+
8+
class RelationMany extends Relation
9+
{
10+
protected function value(Closure $value): Relationship
11+
{
12+
$relation = new Relationship($this->related, $value, $this->links, $this->meta);
13+
$relation->asCollection();
14+
15+
return $relation;
16+
}
17+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace Ark4ne\JsonApi\Descriptors\Relations;
4+
5+
use Ark4ne\JsonApi\Resources\Relationship;
6+
use Closure;
7+
8+
class RelationOne extends Relation
9+
{
10+
protected function value(Closure $value): Relationship
11+
{
12+
return new Relationship($this->related, $value, $this->links, $this->meta);
13+
}
14+
}

src/Descriptors/Resolver.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
namespace Ark4ne\JsonApi\Descriptors;
4+
5+
use Illuminate\Http\Request;
6+
use Illuminate\Support\Collection;
7+
8+
trait Resolver
9+
{
10+
/**
11+
* @param \Illuminate\Http\Request $request
12+
* @param iterable|null $values
13+
*
14+
* @return array|null
15+
*/
16+
protected function resolveValues(Request $request, ?iterable $values): ?array
17+
{
18+
if ($values === null) {
19+
return null;
20+
}
21+
22+
return (new Collection($values))
23+
->reduce(function (Collection $fields, $value, int|string $key) use ($request) {
24+
if (is_int($key) && ($value instanceof Valuable) && is_string($value->retriever())) {
25+
$key = $value->retriever();
26+
}
27+
28+
$fields[$key] = value(
29+
$value instanceof Valuable
30+
? $value->valueFor($request, $this->resource, $key)
31+
: $value
32+
);
33+
34+
return $fields;
35+
}, new Collection)
36+
->toArray();
37+
}
38+
}

0 commit comments

Comments
 (0)