From 1f0cc823ea82d2ad31832918a7f2b2b8009c19c3 Mon Sep 17 00:00:00 2001 From: Joseph Silber Date: Wed, 9 Jul 2014 09:36:58 -0400 Subject: [PATCH] Add casting to Eloquent --- src/Illuminate/Database/Eloquent/Model.php | 143 ++++++++++++++---- .../Database/Eloquent/SoftDeletingTrait.php | 4 + 2 files changed, 121 insertions(+), 26 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Model.php b/src/Illuminate/Database/Eloquent/Model.php index 53748a8933c0..39f28e0c7e89 100755 --- a/src/Illuminate/Database/Eloquent/Model.php +++ b/src/Illuminate/Database/Eloquent/Model.php @@ -129,6 +129,13 @@ abstract class Model implements ArrayAccess, ArrayableInterface, JsonableInterfa */ protected $dates = array(); + /** + * Attributes to cast to their proper type. + * + * @var array + */ + protected $casts = array(); + /** * The relationships that should be touched on save. * @@ -213,6 +220,13 @@ abstract class Model implements ArrayAccess, ArrayableInterface, JsonableInterfa */ protected static $mutatorCache = array(); + /** + * The cache of the cast array for each class. + * + * @var array + */ + protected static $castsCache = array(); + /** * The many to many relationship methods. * @@ -295,6 +309,8 @@ protected static function boot() } static::bootTraits(); + + static::cacheCasts(); } /** @@ -2167,11 +2183,12 @@ public function attributesToArray() // If an attribute is a date, we will cast it to a string after converting it // to a DateTime / Carbon instance. This is so we will get some consistent // formatting while accessing attributes vs. arraying / JSONing a model. - foreach ($this->getDates() as $key) + foreach ($attributes as $key => $value) { - if ( ! isset($attributes[$key])) continue; - - $attributes[$key] = (string) $this->asDateTime($attributes[$key]); + if ($value instanceof DateTime) + { + $attributes[$key] = (string) $value; + } } // We want to spin through all the mutated attributes for this model and call @@ -2337,14 +2354,6 @@ protected function getAttributeValue($key) return $this->mutateAttribute($key, $value); } - // If the attribute is listed as a date, we will convert it to a DateTime - // instance on retrieval, which makes it quite convenient to work with - // date fields without having to create a mutator for each property. - elseif (in_array($key, $this->getDates())) - { - if ($value) return $this->asDateTime($value); - } - return $value; } @@ -2440,18 +2449,7 @@ public function setAttribute($key, $value) return $this->{$method}($value); } - // If an attribute is listed as a "date", we'll convert it from a DateTime - // instance into a form proper for storage on the database tables using - // the connection grammar's date format. We will auto set the values. - elseif (in_array($key, $this->getDates())) - { - if ($value) - { - $value = $this->fromDateTime($value); - } - } - - $this->attributes[$key] = $value; + $this->setRawAttribute($key, $value); } /** @@ -2465,6 +2463,62 @@ public function hasSetMutator($key) return method_exists($this, 'set'.studly_case($key).'Attribute'); } + /** + * Cache the cast array for legacy support. + * + * @return void + */ + static protected function cacheCasts() + { + $class = get_class($instance = new static); + + static::$castsCache[$class] = $instance->casts; + + foreach ($instance->getDates() as $key) + { + static::$castsCache[$class][$key] = 'date'; + } + } + + /** + * Add an attribute to the cast array. + * + * @param string $attribute + * @param string $type + */ + protected function addCast($attribute, $type) + { + $class = get_class($this); + + static::$castsCache[$class][$attribute] = $type; + } + + /** + * Get the cast type for a given attribute. + * + * @param string $attribute + * @return string|null + */ + protected function getCastType($attribute) + { + $casts = $this->getCasts(); + + if (array_key_exists($attribute, $casts)) + { + return $casts[$attribute]; + } + } + + /** + * Get the casts array for the current class. + * + * @return array + */ + protected function getCasts() + { + return static::$castsCache[get_class($this)]; + } + /** * Get the attributes that should be converted to dates. * @@ -2594,7 +2648,7 @@ public function getAttributes() } /** - * Set the array of model attributes. No checking is done. + * Set the array of model attributes. No mutators are called. * * @param array $attributes * @param bool $sync @@ -2602,11 +2656,48 @@ public function getAttributes() */ public function setRawAttributes(array $attributes, $sync = false) { - $this->attributes = $attributes; + $this->attributes = []; + + foreach ($attributes as $key => $value) + { + $this->setRawAttribute($key, $value); + } if ($sync) $this->syncOriginal(); } + /** + * Set a given attribute on the model. No mutators are called. + * + * @param string $key + * @param mixed $value + * @return void + */ + protected function setRawAttribute($key, $value) + { + $this->attributes[$key] = $this->castAttribute($key, $value); + } + + /** + * Cast an attribute to its proper type. + * + * @param string $key + * @param mixed $value + * @return mixed + */ + protected function castAttribute($key, $value) + { + if (is_null($value)) return null; + + if (is_null($type = $this->getCastType($key))) return $value; + + if ($type == 'date') return $this->asDateTime($value); + + settype($value, $type); + + return $value; + } + /** * Get the model's original attribute values. * diff --git a/src/Illuminate/Database/Eloquent/SoftDeletingTrait.php b/src/Illuminate/Database/Eloquent/SoftDeletingTrait.php index 6538b9ab0450..32651411c592 100644 --- a/src/Illuminate/Database/Eloquent/SoftDeletingTrait.php +++ b/src/Illuminate/Database/Eloquent/SoftDeletingTrait.php @@ -17,6 +17,10 @@ trait SoftDeletingTrait { public static function bootSoftDeletingTrait() { static::addGlobalScope(new SoftDeletingScope); + + $instance = new static; + + $instance->addCast($instance->getDeletedAtColumn(), 'date'); } /**