Be Careful to Use Laravel Eloquent’s Accessors, Mutators And Events

Lynn Lin
4 min readMay 2, 2017

Laravel Elqouent provides some convenient ways to manage the models. For example, accessors and mutators can help to format the data attributes every time before retrieving and saving. Events help to do some actions whenever models are modified. However, there is one thing we should take care when using these tools.

Notice the data attributes we can used inside these functions

Accessors

Assume that we have a table named users and every record contains firstName and lastName.

mysql> desc users;
+-----------+------------------+
| Field | Type |
+-----------+------------------+
| userId | int(10) unsigned |
| firstName | varchar(255) |
| lastName | varchar(255) |
| birthday | varchar(8) |
| age | int(11) |
| email | varchar(255) |
| password | varchar(60) |
| createdAt | timestamp |
| updatedAt | timestamp |
+-----------+------------------+

Someday, full name of each user should be returned and we decide not to modify the table schema. We can get the result by iterating all users and concatenate each user's first name and last name. Also, We can use accessors as a tricky way to achieve this.

<?php public function getFullNameAttribute()
{
return $this->attributes['firstName'] . ' ' . $this->attributes['lastName'];
}

Above we write an accessor for the column, fullName which not really exists in the database. It shows that every time we retrieve the model attribute, fullName, accessor will help to concatenate the string automatically.

This seems good and smart but one thing should be taken in mind that Laravel put the data only we select into $this->attributes. That is, if we do not select both firstName and lastName whenever we use fullName, an error will be thrown. See the example below to be more clearly.

Mutators

Mutators encounter the same issue as accessors. When we want to save one attribute depending on other attributes, ensure that those attributes are selected already.

Take the scenario in accessors. This time we want age of each user can be calculated by the birthday. Below shows that when saving age, the value calculated based on the birthday will be returned. And an error will be thrown when birthday is missing. Mutators here are used as a way to save attributes automatically.

<?phppublic function setAgeAttribute($value)
{
$this->attributes['age'] = Carbon::parse($this->attributes['birthday'])->age;
}

This example is not really a good one. Since mutators are not triggered unless attributes (i.e. age) are set, it would be very confusing to write something like $user->update(['age'] => 1000) or $user->update(['age'] => '') just to make mutators work.

So the best way is to save age whenever we save birthday (i.e. an mutator for birthday not age). See as below. But anyway, it is just an example to demonstrate that we can not use attributes we do not select in mutators.

<?phppublic function setBirthdayAttribute($value)
{
$this->attributes['birthday'] = $value;
$this->attributes['age'] = Carbon::parse($value)->age;
}

Events

Same situation occurs in model events. However, PHP will not throw any exceptions this time. When we use the attributes which can not be accessed by either $user->lastName or $user['name'], null will be returned instead. As a result, we just need to make sure that our programs can deal with the null value. For the meaning of events, I think it is acceptable since null may need to be reacted with certain actions in some situations.

Use the tools by what they are designed for

Examples described above may look dumb. Some may think that if accessors contains other attributes, of course they will be select. But no one could guarantee such things especially when the code base becomes larger and more complex. Besides, for the sql performance, we will not query all columns but only those we need. Developers may not notice what have been done in models.

So, in my opinion, I think the best way of using accessors and mutators is to treat them as formatters. Just as what they are introduced in the Laravel documents. And for the events, remember to consider the null value. Then it will be okay when using these tools.

--

--