Eloquent: Mutators

In addition to custom accessors and mutators, Eloquent can also automatically cast date fields to instances or even cast text fields to JSON.

To define an accessor, create a method on your model where Foo is the “studly” cased name of the column you wish to access. In this example, we’ll define an accessor for the first_name attribute. The accessor will automatically be called by Eloquent when attempting to retrieve the value of the first_name attribute:

As you can see, the original value of the column is passed to the accessor, allowing you to manipulate and return the value. To access the value of the accessor, you may access the first_name attribute on a model instance:

  1. $user = App\Models\User::find(1);
  2. $firstName = $user->first_name;

You may also use accessors to return new, computed values from existing attributes:

  1. /**
  2. * Get the user's full name.
  3. *
  4. * @return string
  5. */
  6. public function getFullNameAttribute()
  7. {
  8. return "{$this->first_name} {$this->last_name}";
  9. }

Defining A Mutator

To define a mutator, define a setFooAttribute method on your model where Foo is the “studly” cased name of the column you wish to access. So, again, let’s define a mutator for the first_name attribute. This mutator will be automatically called when we attempt to set the value of the first_name attribute on the model:

  1. <?php
  2. namespace App\Models;
  3. use Illuminate\Database\Eloquent\Model;
  4. class User extends Model
  5. {
  6. /**
  7. * Set the user's first name.
  8. *
  9. * @param string $value
  10. * @return void
  11. */
  12. public function setFirstNameAttribute($value)
  13. {
  14. $this->attributes['first_name'] = strtolower($value);
  15. }
  16. }

The mutator will receive the value that is being set on the attribute, allowing you to manipulate the value and set the manipulated value on the Eloquent model’s internal $attributes property. So, for example, if we attempt to set the first_name attribute to Sally:

  1. $user = App\Models\User::find(1);
  2. $user->first_name = 'Sally';

In this example, the setFirstNameAttribute function will be called with the value Sally. The mutator will then apply the strtolower function to the name and set its resulting value in the internal $attributes array.

By default, Eloquent will convert the created_at and updated_at columns to instances of Carbon, which extends the PHP DateTime class and provides an assortment of helpful methods. You may add additional date attributes by setting the $dates property of your model:

  1. <?php
  2. namespace App\Models;
  3. use Illuminate\Database\Eloquent\Model;
  4. class User extends Model
  5. {
  6. /**
  7. * The attributes that should be mutated to dates.
  8. *
  9. * @var array
  10. */
  11. protected $dates = [
  12. 'seen_at',
  13. ];
  14. }

When a column is considered a date, you may set its value to a UNIX timestamp, date string (Y-m-d), date-time string, or a DateTime / Carbon instance. The date’s value will be correctly converted and stored in your database:

  1. $user = App\Models\User::find(1);
  2. $user->deleted_at = now();
  3. $user->save();
  1. $user = App\Models\User::find(1);
  2. return $user->deleted_at->getTimestamp();

Date Formats

By default, timestamps are formatted as 'Y-m-d H:i:s'. If you need to customize the timestamp format, set the $dateFormat property on your model. This property determines how date attributes are stored in the database:

The $casts property on your model provides a convenient method of converting attributes to common data types. The $casts property should be an array where the key is the name of the attribute being cast and the value is the type you wish to cast the column to. The supported cast types are: integer, real, float, double, decimal:<digits>, string, boolean, object, array, collection, date, datetime, and timestamp. When casting to decimal, you must define the number of digits (decimal:2).

To demonstrate attribute casting, let’s cast the is_admin attribute, which is stored in our database as an integer (0 or 1) to a boolean value:

  1. <?php
  2. namespace App\Models;
  3. use Illuminate\Database\Eloquent\Model;
  4. class User extends Model
  5. {
  6. /**
  7. * The attributes that should be cast.
  8. *
  9. * @var array
  10. */
  11. protected $casts = [
  12. 'is_admin' => 'boolean',
  13. ];
  14. }

Now the is_admin attribute will always be cast to a boolean when you access it, even if the underlying value is stored in the database as an integer:

  1. $user = App\Models\User::find(1);
  2. if ($user->is_admin) {
  3. //
  4. }

Laravel has a variety of built-in, helpful cast types; however, you may occasionally need to define your own cast types. You may accomplish this by defining a class that implements the CastsAttributes interface.

Classes that implement this interface must define a get and set method. The get method is responsible for transforming a raw value from the database into a cast value, while the set method should transform a cast value into a raw value that can be stored in the database. As an example, we will re-implement the built-in json cast type as a custom cast type:

  1. <?php
  2. namespace App\Casts;
  3. use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
  4. class Json implements CastsAttributes
  5. {
  6. /**
  7. * Cast the given value.
  8. *
  9. * @param \Illuminate\Database\Eloquent\Model $model
  10. * @param string $key
  11. * @param mixed $value
  12. * @param array $attributes
  13. * @return array
  14. */
  15. public function get($model, $key, $value, $attributes)
  16. {
  17. return json_decode($value, true);
  18. }
  19. /**
  20. * Prepare the given value for storage.
  21. *
  22. * @param \Illuminate\Database\Eloquent\Model $model
  23. * @param string $key
  24. * @param array $value
  25. * @param array $attributes
  26. * @return string
  27. */
  28. public function set($model, $key, $value, $attributes)
  29. {
  30. return json_encode($value);
  31. }
  32. }

Once you have defined a custom cast type, you may attach it to a model attribute using its class name:

  1. <?php
  2. namespace App\Models;
  3. use App\Casts\Json;
  4. use Illuminate\Database\Eloquent\Model;
  5. class User extends Model
  6. {
  7. /**
  8. * The attributes that should be cast.
  9. *
  10. * @var array
  11. */
  12. protected $casts = [
  13. 'options' => Json::class,
  14. ];
  15. }

Value Object Casting

You are not limited to casting values to primitive types. You may also cast values to objects. Defining custom casts that cast values to objects is very similar to casting to primitive types; however, the set method should return an array of key / value pairs that will be used to set raw, storable values on the model.

As an example, we will define a custom cast class that casts multiple model values into a single Address value object. We will assume the Address value has two public properties: lineOne and lineTwo:

  1. <?php
  2. namespace App\Casts;
  3. use App\Models\Address as AddressModel;
  4. use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
  5. use InvalidArgumentException;
  6. class Address implements CastsAttributes
  7. {
  8. /**
  9. * Cast the given value.
  10. *
  11. * @param \Illuminate\Database\Eloquent\Model $model
  12. * @param string $key
  13. * @param mixed $value
  14. * @param array $attributes
  15. * @return \App\Models\Address
  16. */
  17. public function get($model, $key, $value, $attributes)
  18. {
  19. return new AddressModel(
  20. $attributes['address_line_one'],
  21. $attributes['address_line_two']
  22. );
  23. }
  24. /**
  25. * Prepare the given value for storage.
  26. *
  27. * @param \Illuminate\Database\Eloquent\Model $model
  28. * @param string $key
  29. * @param \App\Models\Address $value
  30. * @param array $attributes
  31. * @return array
  32. */
  33. public function set($model, $key, $value, $attributes)
  34. {
  35. if (! $value instanceof AddressModel) {
  36. throw new InvalidArgumentException('The given value is not an Address instance.');
  37. }
  38. return [
  39. 'address_line_one' => $value->lineOne,
  40. 'address_line_two' => $value->lineTwo,
  41. ];
  42. }
  43. }

When casting to value objects, any changes made to the value object will automatically be synced back to the model before the model is saved:

  1. $user = App\Models\User::find(1);
  2. $user->address->lineOne = 'Updated Address Value';
  3. $user->save();

Inbound Casting

Occasionally, you may need to write a custom cast that only transforms values that are being set on the model and does not perform any operations when attributes are being retrieved from the model. A classic example of an inbound only cast is a “hashing” cast. Inbound only custom casts should implement the CastsInboundAttributes interface, which only requires a set method to be defined.

  1. <?php
  2. use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes;
  3. class Hash implements CastsInboundAttributes
  4. {
  5. /**
  6. * The hashing algorithm.
  7. *
  8. * @var string
  9. */
  10. protected $algorithm;
  11. /**
  12. * Create a new cast class instance.
  13. *
  14. * @param string|null $algorithm
  15. * @return void
  16. */
  17. public function __construct($algorithm = null)
  18. {
  19. $this->algorithm = $algorithm;
  20. }
  21. /**
  22. * Prepare the given value for storage.
  23. *
  24. * @param \Illuminate\Database\Eloquent\Model $model
  25. * @param string $key
  26. * @param array $value
  27. * @param array $attributes
  28. * @return string
  29. */
  30. public function set($model, $key, $value, $attributes)
  31. {
  32. return is_null($this->algorithm)
  33. ? bcrypt($value)
  34. : hash($this->algorithm, $value);
  35. }
  36. }

Cast Parameters

When attaching a custom cast to a model, cast parameters may be specified by separating them from the class name using a : character and comma-delimiting multiple parameters. The parameters will be passed to the constructor of the cast class:

Castables

Instead of attaching the custom cast to your model, you may alternatively attach a class that implements the Illuminate\Contracts\Database\Eloquent\Castable interface:

  1. protected $casts = [
  2. 'address' => \App\Models\Address::class,
  3. ];

Objects that implement the Castable interface must define a castUsing method that returns the class name of the custom caster class that is responsible for casting to and from the Castable class:

  1. <?php
  2. namespace App\Models;
  3. use Illuminate\Contracts\Database\Eloquent\Castable;
  4. use App\Casts\Address as AddressCast;
  5. class Address implements Castable
  6. {
  7. /**
  8. * Get the name of the caster class to use when casting from / to this cast target.
  9. *
  10. * @return string
  11. */
  12. public static function castUsing()
  13. {
  14. return AddressCast::class;
  15. }
  16. }

When using Castable classes, you may still provide arguments in the $casts definition. The arguments will be passed directly to the caster class:

  1. protected $casts = [
  2. 'address' => \App\Models\Address::class.':argument',
  3. ];

Array & JSON Casting

The array cast type is particularly useful when working with columns that are stored as serialized JSON. For example, if your database has a JSON or TEXT field type that contains serialized JSON, adding the array cast to that attribute will automatically deserialize the attribute to a PHP array when you access it on your Eloquent model:

  1. <?php
  2. namespace App\Models;
  3. use Illuminate\Database\Eloquent\Model;
  4. class User extends Model
  5. {
  6. /**
  7. * The attributes that should be cast.
  8. *
  9. * @var array
  10. */
  11. protected $casts = [
  12. 'options' => 'array',
  13. ];
  14. }

Once the cast is defined, you may access the options attribute and it will automatically be deserialized from JSON into a PHP array. When you set the value of the options attribute, the given array will automatically be serialized back into JSON for storage:

  1. $user = App\Models\User::find(1);
  2. $options = $user->options;
  3. $options['key'] = 'value';
  4. $user->options = $options;
  5. $user->save();

To update a single field of a JSON attribute with a more terse syntax, you may use the -> operator:

  1. $user = App\Models\User::find(1);
  2. $user->update(['options->key' => 'value']);

When using the date or datetime cast type, you may specify the date’s format. This format will be used when the :

  1. /**
  2. * The attributes that should be cast.
  3. *
  4. * @var array
  5. */
  6. protected $casts = [
  7. 'created_at' => 'datetime:Y-m-d',
  8. ];

Query Time Casting

Sometimes you may need to apply casts while executing a query, such as when selecting a raw value from a table. For example, consider the following query:

The last_posted_at attribute on the results of this query will be a raw string. It would be convenient if we could apply a date cast to this attribute when executing the query. To accomplish this, we may use the withCasts method:

  1. $users = User::select([
  2. 'users.*',
  3. 'last_posted_at' => Post::selectRaw('MAX(created_at)')
  4. ->whereColumn('user_id', 'users.id')
  5. ])->withCasts([
  6. 'last_posted_at' => 'datetime'