Skip to content

Commit

Permalink
Add casting to Eloquent
Browse files Browse the repository at this point in the history
  • Loading branch information
JosephSilber committed Jul 9, 2014
1 parent e54204b commit de9b1fc
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 25 deletions.
138 changes: 113 additions & 25 deletions src/Illuminate/Database/Eloquent/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,13 @@ abstract class Model implements ArrayAccess, ArrayableInterface, JsonableInterfa
*/
protected $dates = array();

/**
* Attributes to be casted to their proper type.
*
* @var array
*/
protected $casts = array();

/**
* The relationships that should be touched on save.
*
Expand Down Expand Up @@ -213,6 +220,13 @@ abstract class Model implements ArrayAccess, ArrayableInterface, JsonableInterfa
*/
protected static $mutatorCache = array();

/**
* The cache of the casted attributes for each class.
*
* @var array
*/
protected static $castsCache = array();

/**
* The many to many relationship methods.
*
Expand Down Expand Up @@ -295,6 +309,8 @@ protected static function boot()
}

static::bootTraits();

static::cacheCasts();
}

/**
Expand Down Expand Up @@ -2167,11 +2183,11 @@ 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 ($this->getCasts() as $key => $type)
{
if ( ! isset($attributes[$key])) continue;
if ( ! isset($attributes[$key]) || $type != 'date') continue;

$attributes[$key] = (string) $this->asDateTime($attributes[$key]);
$attributes[$key] = (string) $attributes[$key];
}

// We want to spin through all the mutated attributes for this model and call
Expand Down Expand Up @@ -2337,14 +2353,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;
}

Expand Down Expand Up @@ -2440,18 +2448,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);
}

/**
Expand All @@ -2465,6 +2462,62 @@ public function hasSetMutator($key)
return method_exists($this, 'set'.studly_case($key).'Attribute');
}

/**
* Cache the casting 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 casting array.
*
* @param string $attribute
* @param string $type
*/
protected function addCast($attribute, $type)
{
$class = get_class($this);

static::$castsCache[$class][$attribute] = $type;
}

/**
* Get the casting 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 model.
*
* @return array
*/
protected function getCasts()
{
return static::$castsCache[get_class($this)];
}

/**
* Get the attributes that should be converted to dates.
*
Expand Down Expand Up @@ -2594,19 +2647,54 @@ 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
* @return void
*/
public function setRawAttributes(array $attributes, $sync = false)
{
$this->attributes = $attributes;
foreach ($attributes as $key => $value)
{
$this->setRawAttribute($key, $value);

This comment has been minimized.

Copy link
@JoostK

JoostK Jul 9, 2014

This introduces a small change in behaviour, where array keys not passed in will not be unset. Perhaps clear the attributes array before populating it again.

This comment has been minimized.

Copy link
@JosephSilber

JosephSilber Jul 9, 2014

Author Owner

@JoostK Good catch. I pushed an update.

This commit is not ready for a PR (it doesn't even have tests).

I didn't want to steal @daylerees's laravel#4948 party. This is just a sample I made for him.

This comment has been minimized.

Copy link
@daylerees

daylerees Jul 9, 2014

Hey this is open source. If you find a better implementation then go for it dude. :)

}

if ($sync) $this->syncOriginal();
}

/**
* Set a given attribute on the model. No mutators are called.
*
* @param string $attribute
* @param mixed $value
* @return void
*/
protected function setRawAttribute($attribute, $value)
{
$this->attributes[$attribute] = $this->castAttribute($attribute, $value);
}

/**
* Cast an attribute to a new type.
*
* @param string $attribute
* @param mixed $value
* @return mixed
*/
protected function castAttribute($attribute, $value)
{
if (is_null($value)) return null;

if (is_null($type = $this->getCastType($attribute))) return $value;

if ($type == 'date') return $this->asDateTime($value);

settype($value, $type);

return $value;
}

/**
* Get the model's original attribute values.
*
Expand Down
4 changes: 4 additions & 0 deletions src/Illuminate/Database/Eloquent/SoftDeletingTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ trait SoftDeletingTrait {
public static function bootSoftDeletingTrait()
{
static::addGlobalScope(new SoftDeletingScope);

$instance = new static;

$instance->addCast($instance->getDeletedAtColumn(), 'date');
}

/**
Expand Down

0 comments on commit de9b1fc

Please sign in to comment.