Let's get familiar with a few more terms about Eloquent which are Accessors and Mutators or combined, they are called Attributes from Laravel 9.
If you work with projects before Laravel 9, there may be different syntax in older tutorials or examples. I will show you the old syntax at the end of this lesson.
So, Accessors and Mutators are used if you want to change the value when getting or setting the data. Now, why would you do that?
For example, you have a created_at
field but want to show it in a human format.
Because created_at
is automatically casted to a datetime and is a Carbon object, you can use diffForHumans()
directly on the field. Now, of course, you can do that in the blade or the API response directly. But what if you want to reuse it in all cases where you use the model, so it makes sense to have some kind of function inside the Model, which is an Accessor.
So, you define the Accessor as an attribute. Interestingly, the Accessor method name is camel case, but it is returned as snake case.
app/Models/User.php:
use Illuminate\Database\Eloquent\Casts\Attribute; class User extends Authenticatable{ // ... protected function createdDiff(): Attribute { return Attribute::make( get: fn($value) => $this->created_at->diffForHumans(), ); }}
The attribute must return the Eloquent cast Attribute. The attribute may have a get
and a set
function. Now, on the Model, we can call created_diff
to get this attribute value.
Mutators are the opposite of Accessors: they are used when you need to change some value when saving the data.
For example, you're creating a user, and the user is provided with a lowercase name, like "taylor". But you want to respect people and save that with the first uppercase letter anyway.
To do that, you define the name as an attribute, and instead of get, you define a set.
app/Models/User.php:
use Illuminate\Database\Eloquent\Casts\Attribute; class User extends Authenticatable{ // ... protected function name(): Attribute { return Attribute::make( set: fn($value) => ucfirst($value), ); }}
When creating a user, the name will be set to the first uppercase letter before saving it into the database.
Let's get to the example of mixing of Accessor and Mutator, using get
and set
in the same field.
A typical example would be date formatting before and after. For example, you have a birth date formatted in some non-standard format, different from the database. In the database, the date should be saved in Y-m-d
, but your users may provide it in a different format.
So, when saving the data, you must format it into the MySQL format. When getting the data, you need to format it back to the user format, so users won't see how it is stored in the database.
To do that, we would define a birthDate
attribute. The actual user field is birth_date
with underscore in the database.
app/Models/User.php:
use Illuminate\Support\Carbon;use Illuminate\Database\Eloquent\Casts\Attribute; class User extends Authenticatable{ // ... protected function birthDate(): Attribute { return Attribute::make( get: fn($value) => Carbon::createFromFormat('Y-m-d', $value)->format('m/d/Y'), set: fn($value) => Carbon::createFromFormat('m/d/Y', $value)->format('Y-m-d'), ); }}
You can try to create a User.
User::create([ 'name' => 'taylor', 'email' => 'test@test.com', 'password' => bcrypt('password'), 'birth_date' => '01/25/1990',]);
In the database, the birth date is in the correct format.
And, when the field is called, the format is different.
The old syntax still works, but it's not in the official documentation of Laravel: the newer syntax aimed to combine getters and setters in the same function.
So, the old syntax for the function name consists of three parts:
get
or set
Attribute
The example for a birth_date
column would be getBirthDateAttribute()
and setBirthDateAttribute()
.
app/Models/User.php:
use Illuminate\Support\Carbon; class User extends Authenticatable{ // ... public function setBirthDateAttribute($value) { $this->attributes['birth_date'] = Carbon::createFromFormat('m/d/Y', $value)->format('Y-m-d'); } public function getBirthDateAttribute($value) { return Carbon::createFromFormat('Y-m-d', $value)->format('m/d/Y'); }}
Notice: For columns like
created_at
andupdated_at
, be careful when using accessors to override the values of the same fields. For more explanation on why, you can check the YouTube video Eloquent Accessors: Dates, Casts, and "Wrong Way"