@@ -129,6 +129,13 @@ abstract class Model implements ArrayAccess, ArrayableInterface, JsonableInterfa
129129 */
130130 protected $ dates = array ();
131131
132+ /**
133+ * Attributes to cast to their proper type.
134+ *
135+ * @var array
136+ */
137+ protected $ casts = array ();
138+
132139 /**
133140 * The relationships that should be touched on save.
134141 *
@@ -213,6 +220,13 @@ abstract class Model implements ArrayAccess, ArrayableInterface, JsonableInterfa
213220 */
214221 protected static $ mutatorCache = array ();
215222
223+ /**
224+ * The cache of the cast array for each class.
225+ *
226+ * @var array
227+ */
228+ protected static $ castsCache = array ();
229+
216230 /**
217231 * The many to many relationship methods.
218232 *
@@ -295,6 +309,8 @@ protected static function boot()
295309 }
296310
297311 static ::bootTraits ();
312+
313+ static ::cacheCasts ();
298314 }
299315
300316 /**
@@ -2167,11 +2183,12 @@ public function attributesToArray()
21672183 // If an attribute is a date, we will cast it to a string after converting it
21682184 // to a DateTime / Carbon instance. This is so we will get some consistent
21692185 // formatting while accessing attributes vs. arraying / JSONing a model.
2170- foreach ($ this -> getDates () as $ key )
2186+ foreach ($ attributes as $ key => $ value )
21712187 {
2172- if ( ! isset ($ attributes [$ key ])) continue ;
2173-
2174- $ attributes [$ key ] = (string ) $ this ->asDateTime ($ attributes [$ key ]);
2188+ if ($ value instanceof DateTime)
2189+ {
2190+ $ attributes [$ key ] = (string ) $ value ;
2191+ }
21752192 }
21762193
21772194 // We want to spin through all the mutated attributes for this model and call
@@ -2337,14 +2354,6 @@ protected function getAttributeValue($key)
23372354 return $ this ->mutateAttribute ($ key , $ value );
23382355 }
23392356
2340- // If the attribute is listed as a date, we will convert it to a DateTime
2341- // instance on retrieval, which makes it quite convenient to work with
2342- // date fields without having to create a mutator for each property.
2343- elseif (in_array ($ key , $ this ->getDates ()))
2344- {
2345- if ($ value ) return $ this ->asDateTime ($ value );
2346- }
2347-
23482357 return $ value ;
23492358 }
23502359
@@ -2440,18 +2449,7 @@ public function setAttribute($key, $value)
24402449 return $ this ->{$ method }($ value );
24412450 }
24422451
2443- // If an attribute is listed as a "date", we'll convert it from a DateTime
2444- // instance into a form proper for storage on the database tables using
2445- // the connection grammar's date format. We will auto set the values.
2446- elseif (in_array ($ key , $ this ->getDates ()))
2447- {
2448- if ($ value )
2449- {
2450- $ value = $ this ->fromDateTime ($ value );
2451- }
2452- }
2453-
2454- $ this ->attributes [$ key ] = $ value ;
2452+ $ this ->setRawAttribute ($ key , $ value );
24552453 }
24562454
24572455 /**
@@ -2465,6 +2463,62 @@ public function hasSetMutator($key)
24652463 return method_exists ($ this , 'set ' .studly_case ($ key ).'Attribute ' );
24662464 }
24672465
2466+ /**
2467+ * Cache the cast array for legacy support.
2468+ *
2469+ * @return void
2470+ */
2471+ static protected function cacheCasts ()
2472+ {
2473+ $ class = get_class ($ instance = new static );
2474+
2475+ static ::$ castsCache [$ class ] = $ instance ->casts ;
2476+
2477+ foreach ($ instance ->getDates () as $ key )
2478+ {
2479+ static ::$ castsCache [$ class ][$ key ] = 'date ' ;
2480+ }
2481+ }
2482+
2483+ /**
2484+ * Add an attribute to the cast array.
2485+ *
2486+ * @param string $attribute
2487+ * @param string $type
2488+ */
2489+ protected function addCast ($ attribute , $ type )
2490+ {
2491+ $ class = get_class ($ this );
2492+
2493+ static ::$ castsCache [$ class ][$ attribute ] = $ type ;
2494+ }
2495+
2496+ /**
2497+ * Get the cast type for a given attribute.
2498+ *
2499+ * @param string $attribute
2500+ * @return string|null
2501+ */
2502+ protected function getCastType ($ attribute )
2503+ {
2504+ $ casts = $ this ->getCasts ();
2505+
2506+ if (array_key_exists ($ attribute , $ casts ))
2507+ {
2508+ return $ casts [$ attribute ];
2509+ }
2510+ }
2511+
2512+ /**
2513+ * Get the casts array for the current class.
2514+ *
2515+ * @return array
2516+ */
2517+ protected function getCasts ()
2518+ {
2519+ return static ::$ castsCache [get_class ($ this )];
2520+ }
2521+
24682522 /**
24692523 * Get the attributes that should be converted to dates.
24702524 *
@@ -2594,19 +2648,56 @@ public function getAttributes()
25942648 }
25952649
25962650 /**
2597- * Set the array of model attributes. No checking is done .
2651+ * Set the array of model attributes. No mutators are called .
25982652 *
25992653 * @param array $attributes
26002654 * @param bool $sync
26012655 * @return void
26022656 */
26032657 public function setRawAttributes (array $ attributes , $ sync = false )
26042658 {
2605- $ this ->attributes = $ attributes ;
2659+ $ this ->attributes = [];
2660+
2661+ foreach ($ attributes as $ key => $ value )
2662+ {
2663+ $ this ->setRawAttribute ($ key , $ value );
2664+ }
26062665
26072666 if ($ sync ) $ this ->syncOriginal ();
26082667 }
26092668
2669+ /**
2670+ * Set a given attribute on the model. No mutators are called.
2671+ *
2672+ * @param string $key
2673+ * @param mixed $value
2674+ * @return void
2675+ */
2676+ protected function setRawAttribute ($ key , $ value )
2677+ {
2678+ $ this ->attributes [$ key ] = $ this ->castAttribute ($ key , $ value );
2679+ }
2680+
2681+ /**
2682+ * Cast an attribute to its proper type.
2683+ *
2684+ * @param string $key
2685+ * @param mixed $value
2686+ * @return mixed
2687+ */
2688+ protected function castAttribute ($ key , $ value )
2689+ {
2690+ if (is_null ($ value )) return null ;
2691+
2692+ if (is_null ($ type = $ this ->getCastType ($ key ))) return $ value ;
2693+
2694+ if ($ type == 'date ' ) return $ this ->asDateTime ($ value );
2695+
2696+ settype ($ value , $ type );
2697+
2698+ return $ value ;
2699+ }
2700+
26102701 /**
26112702 * Get the model's original attribute values.
26122703 *
0 commit comments