I confess - during five years of working with Laravel, I've been fascinated by its "magic" and how little code is needed to actually make things work. But recently, as my projects grew in size, that "magic" started to become a problem. One of the typical example is Eloquent Accessors, so in this article I will put an argument against using them, and what to do instead.
How Accessors Work and Why They're Great
Let's remember, how accessors work. Imagine that in users DB table you have columns name and surname, but in your table you want to view them as one - as full name. But you don't want to do that string concatenation every time, so accessors are a great solution!
You just define this in app/User.php:
public function getFullNameAttribute()
{
return $this->name . ' ' . $this->surname;
}
And then, whenever you have $user object, you can write this, for example, in Blade:
{{ $user->full_name }}
Eloquent will take care of calling this method, also converting full_name snake case into getFullNameAttribute camel case. Magic, isn't it?
Problem 1: What is This Field?
The problems start usually in bigger projects and bigger teams, when the code is taken over by someone else.
For example, when you have a lot of fields listed in the table, or somewhere else, and you don't obviously see that certain field is an accessor. For example, if you see $user->full_name and $user->home_address nearby, someone would probably expect that both are DB columns, right?
So, accessors hurt readability of the code. It gets even worse if a junior developer (or someone less familiar with Laravel) works on this part of the code - it may take a lot of time for them to understand, why this field doesn't exist in the database, and they may spend minutes/hours digging in the past migrations, looking for full_name column.
Also, these fields are not "clickable" in your IDE, so it's not easy to find what that method is really hiding inside. You need to search all project (or model file) for "getFullName" in your IDE like PhpStorm.
Problem 2: Bigger Accessors May "Hide" Bugs
Here we have a simple example of string concatenation. But quite often I see accessors as full-blown calculation methods with 20+ lines of code logic.
Imagine that this full_name would have bigger logic of international names - in some countries there are three parts of names, also you may include "Mr/Mrs/Ms", initials, lower/upper case letters and much more. While it still works, there's a good chance of bugs in that big logic.
And it's pretty rare that someone would actually write automatic tests for the accessors. If you look from code structure point of view, such bigger calculations should be some kind of a Service class, which would be easier to test by Unit Tests.
Solution: Just Use "Getter" Methods
So, what to do to avoid too much "magic"? Simple - just use methods as methods, without any converting to anything.
So, instead of {{ $user->full_name }} in Blade, wouldn't it be more readable to have this? {{ $user->getFullName() }}.
First, it's immediately understood that it's not a DB column - with () at the end, it's clear that it's a method. Also, your IDE will allow you to click on it, and land inside of that method in your Model. Easier navigation, huh?
In the model, just rename getFullNameAttribute() to getFullName():
public function getFullName()
{
return $this->name . ' ' . $this->surname;
}
Do you agree? Have you ever encountered the problem with "too much magic" in Laravel - with accessors, or other magic methods?
No comments or questions yet...