From 3a09870f697b9ba1a494bc4fe093c32d6288a759 Mon Sep 17 00:00:00 2001 From: Propaganistas Date: Sat, 5 May 2018 20:57:35 +0200 Subject: [PATCH] Define custom casts --- .../Eloquent/Concerns/HasAttributes.php | 68 ++++++++++++++++++- tests/Database/DatabaseEloquentModelTest.php | 18 +++++ 2 files changed, 83 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php index 3ae53bc38f7b..aba48f8cca82 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php +++ b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php @@ -2,6 +2,7 @@ namespace Illuminate\Database\Eloquent\Concerns; +use Closure; use LogicException; use DateTimeInterface; use Illuminate\Support\Arr; @@ -70,6 +71,13 @@ trait HasAttributes */ public static $snakeAttributes = true; + /** + * The registered custom casts. + * + * @var array + */ + protected static $customCasts = []; + /** * The cache of the mutated attributes for each class. * @@ -471,7 +479,7 @@ protected function castAttribute($key, $value) return $value; } - switch ($this->getCastType($key)) { + switch ($cast = $this->getCastType($key)) { case 'int': case 'integer': return (int) $value; @@ -498,9 +506,13 @@ protected function castAttribute($key, $value) return $this->asDateTime($value); case 'timestamp': return $this->asTimestamp($value); - default: - return $value; } + + if ($this->isCustomCast($cast, 'as')) { + return $this->customCastValue($cast, $value, 'as'); + } + + return $value; } /** @@ -566,6 +578,10 @@ public function setAttribute($key, $value) return $this->fillJsonAttribute($key, $value); } + if ($this->isCustomCast($cast = $this->getCastType($key), 'from')) { + $value = $this->customCastValue($cast, $value, 'from'); + } + $this->attributes[$key] = $value; return $this; @@ -774,6 +790,21 @@ protected function asTimestamp($value) return $this->asDateTime($value)->getTimestamp(); } + /** + * Custom cast an attribute to a chosen PHP value. + * + * @param string $cast + * @param mixed $value + * @param string $direction + * @return mixed + */ + protected function customCastValue($cast, $value, $direction) + { + $callback = static::$customCasts[$cast][$direction]; + + return $callback->call($this, $value); + } + /** * Prepare a date for array / JSON serialization. * @@ -852,6 +883,37 @@ public function getCasts() return $this->casts; } + /** + * Register a custom cast Closure. + * + * @param string $cast + * @param \Closure $as + * @param \Closure|null $from + * @return void + */ + public static function extendCasts($cast, Closure $as = null, Closure $from = null) + { + if (! is_null($as)) { + static::$customCasts[$cast]['as'] = $as; + } + + if (! is_null($from)) { + static::$customCasts[$cast]['from'] = $from; + } + } + + /** + * Determine whether a cast name is a custom cast for the given direction. + * + * @param string $cast + * @param string $direction + * @return bool + */ + protected function isCustomCast($cast, $direction) + { + return Arr::has(static::$customCasts, $cast.'.'.$direction); + } + /** * Determine whether a value is Date / DateTime castable for inbound manipulation. * diff --git a/tests/Database/DatabaseEloquentModelTest.php b/tests/Database/DatabaseEloquentModelTest.php index b16d74df08f2..80217133dcc6 100755 --- a/tests/Database/DatabaseEloquentModelTest.php +++ b/tests/Database/DatabaseEloquentModelTest.php @@ -1607,6 +1607,23 @@ public function testModelAttributeCastingFailsOnUnencodableData() $model->getAttributes(); } + public function testModelAttributesCanUseCustomCasts() + { + EloquentModelCastingStub::extendCasts('foo', function ($value) { + return $value[0]; + }, function ($value) { + return [$value, 'foo']; + }); + + $model = new EloquentModelCastingStub; + $model->fooAttribute = 'some_value'; + + $this->assertEquals('some_value', $model->fooAttribute); + + $attributes = $model->getAttributes(); + $this->assertEquals(['some_value', 'foo'], $attributes['fooAttribute']); + } + public function testUpdatingNonExistentModelFails() { $model = new EloquentModelStub; @@ -2048,6 +2065,7 @@ class EloquentModelCastingStub extends Model 'dateAttribute' => 'date', 'datetimeAttribute' => 'datetime', 'timestampAttribute' => 'timestamp', + 'fooAttribute' => 'foo', ]; public function jsonAttributeValue()