lucian.mihalache's avatar

Laravel 9 mutators for custom attributes

Hello world

I am trying to make use of laravel 9's way of doing accessors and mutators but I am having an issue. I have some columns in the database that are very badly named (like FirstN, Adr, CPost) [And no, unfortunately I cannot change the columns]. I want to make some accessors and mutators for these columns that are named right.

So I came up with something like this:

    /**
     * Interact with the user's first name.
     *
     * @param  string  $value
     * @return \Illuminate\Database\Eloquent\Casts\Attribute
     */
    protected function firstName(): Attribute
    {
        return new Attribute(
            get: fn ($value, $attributes) => $attributes['FirstN'],
            set: fn ($value, $attributes) => $attributes['FirstN'] = $value,
        );
    }

I did try the Attribute::make, same result. I tried to use directly $this->attributes instead of $attributes since its using arrow functions and it can use the variables from the scope... And I know that the arrow functions expect a return from the expression and its not used to do logic. I was simply out of ideas and thought why not.

But, of course, it does not work. As far as I understood from the documentation, the fix for my issue is related to Attribute casts, but the documentation is not that helpful...

In Laravel 8 I was using something like this:

    /**
     * @return string
     */
    public function getFirstNameAttribute(): string
    {
        return $this->FirstN;
    }

    /**
     * @param string $value
     * @return void
     */
    public function setFirstNameAttribute(string $value): void
    {
        $this->attributes['FirstN'] = $value;
    }

Ps: I am using $this->attributes and not a direct call to $this->FirstN because I need the thing to be configurable from a config file, this is just an example of the logic.

So... Can somebody guide me into the right direction? How can I do what I could do in laravel 8, but using the new accessors/mutators thing?

[PPS: I want to use it because it removes a lot of lines of code and improves readability if its possible to work in a similar manner as in the docs, that file is big and its hard to go through it. So I thought maybe this feature can improve readability in this case.]

1 like
4 replies
rodrigo.pedra's avatar
Level 56

To enforce custom column name you should return an array keyed by the database column

/**
 * Interact with the user's first name.
 *
 * @param  string  $value
 * @return \Illuminate\Database\Eloquent\Casts\Attribute
 */
protected function firstName(): Attribute
{
    return new Attribute(
        get: fn ($value, $attributes) => $attributes['FirstN'],
        set: fn ($value) => ['FirstN' => $value], // <<< CHANGE HERE
    );
}

reference: https://laravel.com/docs/9.x/eloquent-mutators#mutating-multiple-attributes

5 likes
Artwork's avatar

Just in case, it's worth to mention that the custom attribute method name must not be in format "get$nameAttribute" or "set$nameAttribute" or else the Attribute class's methods won't be called automatically but return as is.

Please or to participate in this conversation.