Laravel Eloquent ignore attribute if not in table during insert

前端 未结 4 1439
不思量自难忘°
不思量自难忘° 2021-02-19 03:44

I have a model Foo that corresponds to a table with the following columns.

id
description
user_id

I\'m setting the attributes of the Foo model individual

相关标签:
4条回答
  • 2021-02-19 04:24

    I understand this question is old but it was in top results for a recent search where I was trying to solve a similar problem and I think that this may be an ideal case for Laravel accessors/mutators. I have tested this on Laravel 5.6 but believe it may work as far back as 4.2.

    By creating a mutator and accessor rather than a public property it will allow adding the field to fillable for mass assignment while still excluding it from the internal attributes (thus preventing it from errantly saving to the DB). I understand the original request excluded mass-assignment but that doesn't necessarily exclude this answer. I think an example will help:

    class Foo extends Model
    {
        //Allow bar in mass assignment
        protected $fillable = [
                "bar"
            ];
    
        /**
         * Store bar in protected variable instead of attributes
         * Because bar is not set in attributes, Laravel will not try to save it to database
         */
        protected $bar;
    
        /**
         * Mutator method to set bar's value
         */
        public function setBarAttribute($value)
        {
            $this->bar = $value;
        }
    
        /**
         * Accessor method to retrieve bar's value
         */ 
        public function getBarAttribute()
        {
            return $this->bar;
        }
    }
    

    When this model is created using mass-assignment the mutator (setBarAttribute) method will be called for bar if it exists in the mass-assigned values. Anytime the bar property is accessed the respective get/set method will be called. Because the mutator does not set the value of bar in the model's internal attributes variable the model will not save bar to the database.

    0 讨论(0)
  • 2021-02-19 04:25

    I know it is too late, but you can register the saving operation by override the boot function in your model:

    protected static function boot() {
        parent::boot();
    
        static::saving(function($model) {
            $savable = [...];
            if (count($savable) > 0) {
                $model->attributes = array_intersect_key($model->attributes, array_flip($savable));
            }
        });
    }
    

    This is untested code, but the idea is to remove the attributes that have not intersect with the variable savable before saving the model. The variable savable is an array of the savable attributes. For example $savable = ['foo', 'bar'] will save only the foo and bar attributes.

    Pros: You can mass assign any attributes that you want without harm to fillable or guarded.

    Cons: The attributes that are not marked as savable, will be deleted from the model after saving.

    0 讨论(0)
  • 2021-02-19 04:31

    Add the $fillable and the model will ignore everything not in it (instead of giving an error). Using the constructor function to fill all the columns is optional.

    class Foo extends Model
    {
        protected $fillable = ['id', 'description', 'user_id'];
    }
    
    $f = new Foo(['id' => 1, 'description' => "hello monkey", 'user_id' => 55, 'bar' => 'wow']); // should work w/o error, but it ignores the 'bar'.
    
    0 讨论(0)
  • 2021-02-19 04:49

    Just add $bar as an attribute in your foo class:

    class Foo extends Model
    {
    
      public $bar;
      //...
    

    now you can use save() and Laravel will not try to store bar in the DB.


    Explanation:

    If you call save() on a model, only those attributes that are in the array $model->attributes will be saved to the database. If you define $bar as an attribute in the class Foo, then $foo->bar ="xyz" will never end up in the array $model->attributes.

    However, if you do not have declared such an attribute for Foo, then __set() is called because you try to save something in an inaccessible property.

    You may check out Laravel\Illuminate\Database\Eloquent\Model.php:

    /**
         * Dynamically set attributes on the model.
         *
         * @param  string  $key
         * @param  mixed  $value
         * @return void
         */
        public function __set($key, $value)
        {
            $this->setAttribute($key, $value);
        }
    

    which basically calls

    $this->attributes[$key] = $value;
    

    from Laravel\Illuminate\Database\Eloquent\Concerns\HasAttributes.php.

    Now $foo->bar ="xyz" will end up beeing in $foo->attribute['bar'] and this is why save() crashes with ..this column does not exists...

    0 讨论(0)
提交回复
热议问题