Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LARAVEL how to change $fillable in Model from trait?

I have in model:

use seoTrait;

protected $fillable = [
    'name', 'title', 'description'
];

I created trait "seoTrait" which need "seoMeta" in $fillable.

Now I add :

protected $fillable = [
    'name', 'title', 'description', 'seoMeta'
];

But is it possible in trait "seoTrait" add something to $fillable ?

like image 566
fico7489 Avatar asked Feb 25 '16 09:02

fico7489


4 Answers

For anyone that came here looking for an answer; I believe that there is a better way than constructor overloading.

Others have pointed out that Laravel supports bootable model traits, using the boot<TraitName> method you can set up the trait for usage. However, the boot<TraitName> method has to be static, so you cannot alter any non-static properties of the model.

However, I found out that there is a non-static equivalent to the boot<TraitName> method; the initialize<TraitName> method.

Internally, when Eloquent boots up a model, it also registers all initialize methods for the model, based on the methods found in the traits that the model uses. Then upon instantiating that model, the initialize methods are fired.

An example of usage, based on the problem stated in this question:

trait SEOTrait
{
    /**
     * This method is called upon instantiation of the Eloquent Model.
     * It adds the "seoMeta" field to the "$fillable" array of the model.
     *
     * @return void
     */
    public function initializeSEOTrait()
    {
        $this->fillable[] = 'seoMeta';
    }
}

Sadly there isn't any information in the official documentation about either bootable or initializable traits. However, I still find it really cool that both functionalities exist.

like image 115
OneBigOwnage Avatar answered Nov 16 '22 16:11

OneBigOwnage


I had the same requirement. However, I didn't want to declare a constructor in my trait, as this leads to very unexpected behaviour - what if another trait also declares a constructor?

As pointed out by PeterPan666, we can't use the static trait boot() method to directly set the non-static fillable property - but we can utilise Laravel Model Events, which receive an instance of the model.

For example:

public static function bootSeoTrait() : void
{
    static::retrieved(function($model) {
        $model->fillable = array_merge($model->fillable, ['seoMeta']);
    });
}

Update

Although the $fillable property is protected, the GuardsAttributes trait - which the Illuminate\Database\Eloquent\Model uses - contains the following setter:

/**
 * Set the fillable attributes for the model.
 *
 * @param  array  $fillable
 * @return $this
 */
public function fillable(array $fillable)
{
    $this->fillable = $fillable;

    return $this;
}

So, we can set the $fillable protected property from our model instance in the retrieved event.

like image 21
BrynJ Avatar answered Nov 16 '22 16:11

BrynJ


Since Laravel 5.7, you can initialize a model trait with a magic initializeTraitName method on the trait which allow access to the model instance.

trait SeoTrait
{
    public function initializeSeoTrait()
    {
        $this->fillable[] = 'seoMeta';
    }
}
like image 4
Elie Faës Avatar answered Nov 16 '22 14:11

Elie Faës


You can use constructor inside your trait, but it is important to send fillable attributes to parent constructor Illuminate\Database\Eloquent\Modal

    parent::__construct($attributes);

Here is example code:

public function __construct(array $attributes = []) {
    parent::__construct($attributes);
    $this->fillable[] = 'seoMeta';
}
like image 1
DokiCRO Avatar answered Nov 16 '22 14:11

DokiCRO