eithed
eithed

Reputation: 4320

Eloquent accessors and property naming

I'm defining my class as such:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Foo extends Model
{
    use HasFactory;

    protected $fillable = [
        'id', 'first_name'
    ];

    public function getFirstNameAttribute($value = null)
    {
        return ucfirst($value);
    }
}

Then I populate my DB with entry for that model:

\App\Models\Foo::create(['first_name' => 'foo']);

Then I'm calling accessor as follows:

\App\Models\Foo::first()->first_name;
=> "Foo"

which is expected

and

\App\Models\Foo::first()->firstName;
=> ""

which is unexpected (but understandable as even though getFirstNameAttribute method is being called, DB property firstName is being accessed and that doesn't exist)

I'd like to ensure that in both cases correct property (first_name) is being accessed.

Does this mean that ($value = null) is unreliable and one should always rely on $this->attributes['first_name']?

As such, this code becomes:

public function getFirstNameAttribute($value = null)
{
    return ucfirst($this->attributes['first_name']);
}

Though I'd probably expect

>>> \App\Models\Foo::first()->first_____na______me;
=> "Foo"

to not do this.

Upvotes: 3

Views: 596

Answers (1)

apokryfos
apokryfos

Reputation: 40683

When you try to access a model attribute then Laravel will (a) try to retrieve the given attribute and (b) look for an accessor which matches the attribute you attempted to access.

However the convention is that database fields are named using snake_case and PHP functions are named using camelCase (with one humped camels). The rule for naming accessors is get{AttributeName}Attribute which means that to find whether there's an accessor for first_name then Laravel will convert the first_name to StudlyCase (or two humped CamelCase or PascalCase) and look for a function called getFirstNameAttribute and then call it using the underlying database value as a parameter. It will always do this regardless of what the attribute requests was named. It does not actually have to be snake_case

However the problems are:

  1. PHP function names are not case sensitive. This means that calling getFirstNameAttribute as getFirstNaMeAttribute will work just fine and call the same method
  2. an accessor for FirstName will have the same name as an accessor for first_name

Therefore your ->first_na__me call will look for an accessor getFirstNaMeAttribute and ->firstName will do the same, however only first_name will have an actual underlying database value to pass in the $value parameter.

It's easy to determine what accessor first_name corresponds to but it's really hard to determine what database value getFirstNameAttribute corresponds to because php function names are not case sensitive so you can go one way but not the other. Therefore it's just easier to get the literal database field and then call the accessor rather than associate an accessor with a database field

Upvotes: 2

Related Questions