The Biggest Problem with Eloquent Accessors “Magic”

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?

Like our articles?
Check out our Laravel online courses!


        • Oh right, good argument.
          Personally, for APIs I’m used to Resources class where I define each field individually, so auto including in json for accessors was never an issue for me.

          • Yeah sorry for the mess. So you suggest typing every field by hand when you use resources. Doesnt make this the framework useless? I mean it should do something megically. That is why we develop libraries think.

      • sir gave issue in query select data from join query used in blade here is i gave one column which have json data how i can use the json

  1. It depends. I personally do that in resources, yes, because I want to know exactly what my API returns, so hide some fields, maybe not expose ID etc. But that’s more of a personal preference, generally that auto-casting is a good argument for using Accessors.

  2. I think I’ve read somewhere, but then I couldn’t find it again… that Accessors are cached. I.e., instead of doeint his:
    public function getCalculatedProp()
    if (! isset($this->calculatedProp)) {
    $this->calculatedProp = $this->doGetCalculatedProp();

    return $this->calculatedProp;
    protected function doGetCalculatedProp()
    // some heavy calculation goes here;

    return $res;

    … instead of that, you just put the heavy calculation into the ::getCalculatedPropAttribute() and it does runtime caching for you.

    NOW, THE IMPORTANT: For quite some time I was sure it was true… but recently I did a test: put a `dump(‘here’)` into accessor’s body and addressed this accessor several time in tinker. And it dumped every time! So, I’m now confused =)

  3. I think for big projects/teams it’s better to stay away from framework specific magical features when coding and play readability first, this certainly will help the team to expand/change with out much time lost on adapting new comers to framework specific magic (of course unless the feature is very clear, readable and not magical).

  4. If you’re using Laravel you should be familiar with accessors and mutators. If another team comes in, they should be familiar with them too.

    As soon as you see a model attribute you know that it might be a “virtual” one. Leverage PHPDoc to make it clear if you want. We want/need this imo.

    If you’ve got complexe logic, isolate it somewhere else: dedicated function(s) on the model, service class, trait, whatever suits you.

    But Laravel is nice and let you use whatever you like really.

  5. You can use a getter (an accessor) in the collections. For example, this code would not work:
    $users = User::orderBy(‘full_name’)->get();

    But, this one would:
    $users = User::all()->sortBy(‘full_name’);

    So after getting the collection of users, you can then sort it by using sortBy() function. Not only sort, but ‘filter’, ‘where’, ‘count’, ‘has’ and many other methods of a collection are become available

    • I cannot digest that using accessor for a simple task like this which should be handle in the SQL. But yes it is kinda useful to use the accessor for Collection Methods

  6. What do ya’ll think about namespacing the accessor, like this:

    public function getAccFullNameAttribute()
    return $this->name . ‘ ‘ . $this->surname;

    So it reads like this:

    {{ $user->acc_full_name }}

    This way you can spot accessors easier as a developer as this seems like the most consequential issue at hand.

    What do you think?

  7. I agree about the readability etc. but as far as I know it’s a recommended way in a Laravel to use getters and not methods. Pros of this approach: $appends – other than that, I don’t see any pros and personally prefer methods instead of getters.

  8. I agree with this logic, however I still wanted something more in between magic and readable.

    Heres what I came up with:

    In the Model, use getFullNameAttribute(), for example. This will not exist as a database column and so will not be automatically invoked in the model.

    But then add in the model:

    “protected $appends [‘fullName’];”

    Then the above method will be called and add the fullName property to the result object.

    So, it doesnt have to be explicitly called in your code but also does not represent a database column, so that should reduce confusion to some extent. Bonus: it gets included in raw JSON output and when using relationships.

    This could be taken further and include a naming convention as previously mentioned such as:
    protected $appends = [‘computedFullName’];
    This way even someone new to Laravel would still likely check the Model, especially since the class would be referenced in the controller code they are probably looking at, and then see that its a computed field.

  9. @povilas since custom casts came out, just wondering if your opinion changed on this?

    (off-topic: your comment form rejected my valid email address as invalid format is, using another)

  10. Thanks for the post and definitely agree with you but anyway there must be some benefit using that magic method. it seems like there are tasks for like ide-helper to find that magic method out to link it’s original place….

  11. I absolutely agree with Povilas. A huge amount of magic inherent in the model is a banal enticement for beginners. These newbies are doing their home projects and are happy. Teamwork on a business project requires a professional approach and good practices. Any team lead should slap junior’s hands for using this kind of magic.

  12. I noticed for the following the getters were not applied. I did not deep dive into the code underneath but I won’t be using getters and setters mostly for my projects.


    Also, ->update() does not trigger mutators.

  13. {

    public function report(SaleRequest $request)

    $sales = $request->sales;
    foreach ($request->sales as $key => $value) {

    $product_id = $value[‘id’];
    $quantity = $value[‘quantity’];
    $warehouse = ‘warehouse’;

    $calculate = ProductMaterial::query()
    DB::raw(‘warehouse_materials.buy_price as price’),
    DB::raw(“product_material.quantity as quantity”),
    DB::raw(‘SUM(warehouse_materials.reminder) as totalreminder’),
    DB::raw(‘ as material’),
    DB::raw(‘ as product’),
    DB::raw(“ as ‘$warehouse'”),
    DB::raw(“product_material.quantity*’$quantity’ as totalqty”),
    DB::raw(“product_material.quantity*’$quantity’ – SUM(warehouse_materials.reminder)”),
    DB::raw(“product_material.quantity*’$quantity’ – warehouse_materials.reminder”),
    ->join(‘materials’, ‘product_material.material_id’, ‘=’, ‘’)
    ->join(‘warehouse_materials’, ‘product_material.material_id’, ‘=’, ‘warehouse_materials.material_id’)
    ->join(‘products’, ‘product_material.product_id’, ‘=’, ‘’)
    ->join(‘ware_houses’, ‘warehouse_materials.ware_house_id’, ‘=’, ‘’)
    ->orderBy(‘warehouse_materials.material_id’, ‘asc’)
    ->orderBy(‘’, ‘asc’)
    ->where(‘product_material.product_id’, $product_id)

    $product = Product::with(‘productMaterials’)->where(‘id’, $product_id)->get();


    return response()->json([
    ‘success’ => true
    it should calculate 2 id , but calculate only one


Please enter your comment!
Please enter your name here