Ali Raza
Ali Raza

Reputation: 932

Dynamically add accessor methods in Laravel Model Class

I've got the function names constructed however I want in an array which are dynamic in nature meaning they could be 2 or 10 like so:

enter image description here

Result

I want them to reside in model class (e.g: User) like this:

public function getEmailVerifiedAtAttribute($value)
{
  // ...
}

public function getCreatedAtAttribute($value)
{
  // ...
}

public function getUpdatedAtAttribute($value)
{
  // ...
}

// ... If there were more in array they would have been constructed dynamically as well.

If we can avoid the eval please!!

Upvotes: 1

Views: 955

Answers (1)

apokryfos
apokryfos

Reputation: 40663

You might have some limited success by doing something like this in your model:

public function hasGetMutator($key) {
   return parent::hasGetMutator($key) || in_array('get'.Str::studly($key).'Attribute', $youArratOfDynamicMutators);
}

protected function mutateAttribute($key, $value)
{
        if (parent::hasGetMutator($key)) {
           return parent::mutateAttribute($key, $value);
        }
        // Mutate your value here
        return $value;
}

What this does is override the method hasGetMutator which usually just checks if the function 'get'.Str::studly($key).'Attribute' exists in the class to also return true if that function name exists in your array, and also modifies the mutateAttribute function to do your custom mutation (in addition to doing the default ones).

However if your mutation is a standard one then I do recommend using a custom cast instead:

<?php

namespace App\Casts;

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;

class MyCustomCast implements CastsAttributes
{

    public function get($model, $key, $value, $attributes) {
        // Do the cast
        return $value;
    }

    //Optional
    public function set($model, $key, $value, $attributes)
    {
        // Reverse the cast 
        return $value;
    }
}

To get this to work for dynamic attributes you can add this to your model:

protected function __construct(array $attributes = [])
{
    parent::__construct($attributes);
    $this->casts = array_merge($this->casts, [
        'customCastColumn1' => MyCustomCast::class,
         // ...
    ]);
}

This will add the required casts to the model when it gets constructed.

Upvotes: 2

Related Questions