Skip to content

Commit d23cf37

Browse files
authored
Merge pull request #214 from hypervel/feature/data-object-auto-cast
feat: support auto-cast in data object and eloquent caster
2 parents d8045c8 + 1a86b39 commit d23cf37

File tree

5 files changed

+601
-29
lines changed

5 files changed

+601
-29
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Hypervel\Database\Eloquent\Concerns;
6+
7+
use Hyperf\Contract\CastsAttributes;
8+
use Hypervel\Support\DataObject;
9+
use InvalidArgumentException;
10+
11+
class AsDataObject implements CastsAttributes
12+
{
13+
protected static $reflectionCache = [];
14+
15+
public function __construct(
16+
protected string $argument
17+
) {
18+
if (! is_subclass_of($this->argument, DataObject::class)) {
19+
throw new InvalidArgumentException(sprintf(
20+
'The given class %s is not a subclass of %s.',
21+
$this->argument,
22+
DataObject::class
23+
));
24+
}
25+
}
26+
27+
/**
28+
* Cast the given value.
29+
*
30+
* @param array<string, mixed> $attributes
31+
* @param mixed $model
32+
*/
33+
public function get(
34+
$model,
35+
string $key,
36+
mixed $value,
37+
array $attributes,
38+
): ?DataObject {
39+
if (! $data = json_decode((string) $value, true)) {
40+
return null;
41+
}
42+
43+
return call_user_func_array(
44+
[$this->argument, 'make'],
45+
[$data, true]
46+
);
47+
}
48+
49+
/**
50+
* Prepare the given value for storage.
51+
*
52+
* @param array<string, mixed> $attributes
53+
* @param mixed $model
54+
*/
55+
public function set(
56+
$model,
57+
string $key,
58+
mixed $value,
59+
array $attributes,
60+
): string {
61+
return json_encode($value);
62+
}
63+
64+
/**
65+
* Specify a custom caster class for the data object.
66+
*/
67+
public static function castUsing(string $class): string
68+
{
69+
return sprintf('%s:%s', static::class, $class);
70+
}
71+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Hypervel\Database\Eloquent\Concerns;
6+
7+
use Hyperf\Contract\Castable;
8+
use Hyperf\Contract\CastsAttributes;
9+
use Hyperf\Contract\CastsInboundAttributes;
10+
11+
trait HasAttributes
12+
{
13+
/**
14+
* The cache of the casters.
15+
*/
16+
protected static array $casterCache = [];
17+
18+
/**
19+
* The cache of the casts.
20+
*/
21+
protected static array $castsCache = [];
22+
23+
/**
24+
* Resolve the custom caster class for a given key.
25+
*/
26+
protected function resolveCasterClass(string $key): CastsAttributes|CastsInboundAttributes
27+
{
28+
$castType = $this->getCasts()[$key];
29+
if ($caster = static::$casterCache[static::class][$castType] ?? null) {
30+
return $caster;
31+
}
32+
33+
$arguments = [];
34+
35+
$castClass = $castType;
36+
if (is_string($castClass) && str_contains($castClass, ':')) {
37+
$segments = explode(':', $castClass, 2);
38+
39+
$castClass = $segments[0];
40+
$arguments = explode(',', $segments[1]);
41+
}
42+
43+
if (is_subclass_of($castClass, Castable::class)) {
44+
$castClass = $castClass::castUsing();
45+
}
46+
47+
if (is_object($castClass)) {
48+
return static::$casterCache[static::class][$castType] = $castClass;
49+
}
50+
51+
return static::$casterCache[static::class][$castType] = new $castClass(...$arguments);
52+
}
53+
54+
/**
55+
* Get the casts array.
56+
*/
57+
public function getCasts(): array
58+
{
59+
if (! is_null($cache = static::$castsCache[static::class] ?? null)) {
60+
return $cache;
61+
}
62+
63+
if ($this->getIncrementing()) {
64+
return static::$castsCache[static::class] = array_merge([$this->getKeyName() => $this->getKeyType()], $this->casts, $this->casts());
65+
}
66+
67+
return static::$castsCache[static::class] = $this->casts;
68+
}
69+
70+
/**
71+
* Get the attributes that should be cast.
72+
*
73+
* @return array<string, string>
74+
*/
75+
protected function casts(): array
76+
{
77+
return [];
78+
}
79+
}

src/core/src/Database/Eloquent/Model.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Hyperf\Stringable\Str;
99
use Hypervel\Broadcasting\Contracts\HasBroadcastChannel;
1010
use Hypervel\Context\Context;
11+
use Hypervel\Database\Eloquent\Concerns\HasAttributes;
1112
use Hypervel\Database\Eloquent\Concerns\HasCallbacks;
1213
use Hypervel\Database\Eloquent\Concerns\HasObservers;
1314
use Hypervel\Database\Eloquent\Concerns\HasRelations;
@@ -65,6 +66,7 @@
6566
*/
6667
abstract class Model extends BaseModel implements UrlRoutable, HasBroadcastChannel
6768
{
69+
use HasAttributes;
6870
use HasCallbacks;
6971
use HasRelations;
7072
use HasRelationships;

0 commit comments

Comments
 (0)