8000 [6.x] Added creation of custom Cast classes for Eloquent · laravel/framework@105b024 · GitHub
[go: up one dir, main page]

Skip to content

Commit 105b024

Browse files
author
Andrey Helldar
committed
[6.x] Added creation of custom Cast classes for Eloquent
1 parent 4d4c098 commit 105b024

File tree

6 files changed

+268
-16
lines changed

6 files changed

+268
-16
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace Illuminate\Contracts\Database\Eloquent;
4+
5+
interface Castable
6+
{
7+
/**
8+
* Get the mutated value.
9+
*
10+
* @param mixed $value
11+
*
12+
* @return mixed
13+
*/
14+
public function handle($value = null);
15+
}

src/Illuminate/Database/Eloquent/Concerns/HasCasts.php

Lines changed: 78 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
namespace Illuminate\Database\Eloquent\Concerns;
44

5+
use Illuminate\Container\Container;
6+
use Illuminate\Contracts\Container\BindingResolutionException;
7+
use Illuminate\Contracts\Database\Eloquent\Castable;
58
use Illuminate\Contracts\Support\Arrayable;
69
use Illuminate\Database\Eloquent\JsonEncodingException;
710
use Illuminate\Support\Collection;
@@ -25,11 +28,13 @@ trait HasCasts
2528
*/
2629
public function hasCast($key, $types = null)
2730
{
28-
if (array_key_exists($key, $this->getCasts())) {
29-
return $types ? in_array($this->getCastType($key), (array) $types, true) : true;
31+
if (! array_key_exists($key, $this->getCasts())) {
32+
return false;
3033
}
3134

32-
return false;
35+
return $types
36+
? in_array($this->getCastType($key), (array) $types, true)
37+
: true;
3338
}
3439

3540
/**
@@ -39,11 +44,14 @@ public function hasCast($key, $types = null)
3944
*/
4045
public function getCasts()
4146
{
42-
if ($this->getIncrementing()) {
43-
return array_merge([$this->getKeyName() => $this->getKeyType()], $this->casts);
44-
}
47+
return $this->getIncrementing()
48+
? array_merge([$this->getKeyName() => $this->getKeyType()], $this->casts)
49+
: $this->casts;
50+
}
4551

46-
return $this->casts;
52+
public function getCast($key)
53+
{
54+
return $this->getCasts()[$key];
4755
}
4856

4957
/**
@@ -52,6 +60,8 @@ public function getCasts()
5260
* @param string $key
5361
* @param mixed $current
5462
*
63+
* @throws BindingResolutionException
64+
*
5565
* @return bool
5666
*/
5767
public function originalIsEquivalent($key, $current)
@@ -87,6 +97,8 @@ public function originalIsEquivalent($key, $current)
8797
* @param array $attributes
8898
* @param array $mutatedAttributes
8999
*
100+
* @throws BindingResolutionException
101+
*
90102
* @return array
91103
*/
92104
protected function addCastAttributesToArray(array $attributes, array $mutatedAttributes)
@@ -106,12 +118,11 @@ protected function addCastAttributesToArray(array $attributes, array $mutatedAtt
106118
// If the attribute cast was a date or a datetime, we will serialize the date as
107119
// a string. This allows the developers to customize how dates are serialized
108120
// into an array without affecting how they are persisted into the storage.
109-
if ($attributes[$key] &&
110-
($value === 'date' || $value === 'datetime')) {
121+
if (($value === 'date' || $value === 'datetime')) {
111122
$attributes[$key] = $this->serializeDate($attributes[$key]);
112123
}
113124

114-
if ($attributes[$key] && $this->isCustomDateTimeCast($value)) {
125+
if ($this->isCustomDateTimeCast($value)) {
115126
$attributes[$key] = $attributes[$key]->format(explode(':', $value, 2)[1]);
116127
}
117128

@@ -129,9 +140,11 @@ protected function addCastAttributesToArray(array $attributes, array $mutatedAtt
129140
* @param string $key
130141
* @param mixed $value
131142
*
143+
* @throws BindingResolutionException
144+
*
132145
* @return mixed
133146
*/
134-
protected function castAttribute($key, $value)
147+
protected function castAttribute($key, $value = null)
135148
{
136149
if (is_null($value)) {
137150
return $value;
@@ -146,7 +159,7 @@ protected function castAttribute($key, $value)
146159
case 'double':
147160
return $this->fromFloat($value);
148161
case 'decimal':
149-
return $this->asDecimal($value, explode(':', $this->getCasts()[$key], 2)[1]);
162+
return $this->asDecimal($value, explode(':', $this->getCast($key), 2)[1]);
150163
case 'string':
151164
return (string) $value;
152165
case 'bool':
@@ -167,7 +180,9 @@ protected function castAttribute($key, $value)
167180
case 'timestamp':
168181
return $this->asTimestamp($value);
169182
default:
170-
return $value;
183+
return $this->isCustomCastable($key)
184+
? $this->fromCustomCastable($key, $value)
185+
: $value;
171186
}
172187
}
173188

@@ -180,15 +195,17 @@ protected function castAttribute($key, $value)
180195
*/
181196
protected function getCastType($key)
182197
{
183-
if ($this->isCustomDateTimeCast($this->getCasts()[$key])) {
198+
$cast = $this->getCast($key);
199+
200+
if ($this->isCustomDateTimeCast($cast)) {
184201
return 'custom_datetime';
185202
}
186203

187-
if ($this->isDecimalCast($this->getCasts()[$key])) {
204+
if ($this->isDecimalCast($cast)) {
188205
return 'decimal';
189206
}
190207

191-
return trim(strtolower($this->getCasts()[$key]));
208+
return trim(strtolower($cast));
192209
}
193210

194211
/**
@@ -250,6 +267,21 @@ protected function isDateAttribute($key)
250267
$this->isDateCastable($key);
251268
}
252269

270+
/**
271+
* Is the checked value a custom Cast.
272+
*
273+
* @param string $key
274+
*
275+
* @return bool
276+
*/
277+
protected function isCustomCastable($key)
278+
{
279+
return is_subclass_of(
280+
$this->getCast($key),
281+
Castable::class
282+
);
283+
}
284+
253285
/**
254286
* Determine whether a value is Date / DateTime castable for inbound manipulation.
255287
*
@@ -273,4 +305,34 @@ protected function isJsonCastable($key)
273305
{
274306
return $this->hasCast($key, ['array', 'json', 'object', 'collection']);
275307
}
308+
309+
/**
310+
* Getting the execution result from a user Cast object.
311+
*
312+
* @param string $key
313+
* @param null $value
314+
*
315+
* @throws BindingResolutionException
316+
*
317+
* @return mixed
318+
*/
319+
protected function fromCustomCastable($key, $value = null)
320+
{
321+
return $this
322+
->normalizeHandlerToCallable($key)
323+
->handle($value);
324+
}
325+
326+
/**
327+
* @param string $key
328+
*
329+
* @throws BindingResolutionException
330+
*
331+
* @return Castable
332+
*/
333+
protected function normalizeHandlerToCallable($key)
334+
{
335+
return Container::getInstance()
336+
->make($this->getCast($key));
337+
}
276338
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
namespace Illuminate\Foundation\Console;
4+
5+
use Illuminate\Console\GeneratorCommand;
6+
7+
class CastMakeCommand extends GeneratorCommand
8+
{
9+
/**
10+
* The console command name.
11+
*
12+
* @var string
13+
*/
14+
protected $name = 'make:cast';
15+
16+
/**
17+
* The console command description.
18+
*
19+
* @var string
20+
*/
21+
protected $description = 'Create a new cast mutator';
22+
23+
/**
24+
* The type of class being generated.
25+
*
26+
* @var string
27+
*/
28+
protected $type = 'Cast';
29+
30+
/**
31+
* Get the stub file for the generator.
32+
*
33+
* @return string
34+
*/
35+
protected function getStub()
36+
{
37+
return __DIR__.'/stubs/cast.stub';
38+
}
39+
40+
/**
41+
* Get the default namespace for the class.
42+
*
43+
* @param string $rootNamespace
44+
*
45+
* @return string
46+
*/
47+
protected function getDefaultNamespace($rootNamespace)
48+
{
49+
return $rootNamespace.'\Http\Casts';
50+
}
51+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace DummyNamespace;
4+
5+
use Illuminate\Contracts\Database\Eloquent\Castable;
6+
7+
class DummyClass implements Castable
8+
{
9+
/**
10+
* Get the mutated value.
11+
*
12+
* @param mixed $value
13+
*
14+
* @return mixed
15+
*/
16+
public function handle($value = null)
17+
{
18+
return $value;
19+
}
20+
}

src/Illuminate/Foundation/Providers/ArtisanServiceProvider.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Illuminate\Database\Console\Seeds\SeedCommand;
1414
use Illuminate\Database\Console\Seeds\SeederMakeCommand;
1515
use Illuminate\Database\Console\WipeCommand;
16+
use Illuminate\Foundation\Console\CastMakeCommand;
1617
use Illuminate\Foundation\Console\ChannelMakeCommand;
1718
use Illuminate\Foundation\Console\ClearCompiledCommand;
1819
use Illuminate\Foundation\Console\ConfigCacheCommand;
@@ -130,6 +131,7 @@ class ArtisanServiceProvider extends ServiceProvider implements DeferrableProvid
130131
'MailMake' => 'command.mail.make',
131132
'MiddlewareMake' => 'command.middleware.make',
132133
'ModelMake' => 'command.model.make',
134+
'CastMake' => 'command.cast.make',
133135
'NotificationMake' => 'command.notification.make',
134136
'NotificationTable' => 'command.notification.table',
135137
'ObserverMake' => 'command.observer.make',
@@ -486,6 +488,13 @@ protected function registerModelMakeCommand()
486488
});
487489
}
488490

491+
protected function registerCastMakeCommand()
492+
{
493+
$this->app->singleton('command.cast.make', function ($app) {
494+
return new CastMakeCommand($app['files']);
495+
});
496+
}
497+
489498
/**
490499
* Register the command.
491500
*

0 commit comments

Comments
 (0)
0