framework: Model $attributes gives error with Model::firstOrCreate()

  • Laravel Version: 7.11.0
  • PHP Version: 7.4.4
  • Database Driver & Version: MySql 5.7

Description:

model $attributes gives error when I try to use Model::firstOrCreate()

Steps To Reproduce:

I have this on a model:

    protected $attributes = [
        'invoicing' => [
            'calc_due_days_from' => 'invoice_date',
            'invoice_date_num_days' => 30,
            'before_start_num_days' => 48,
            'currency_code' => 'SEK',
            'currency_symbol' => 'kr',
            'invoice_note' => null,
            'inv_prefix' => null,
            'inv_suffix' => null,
            'inv_counter' => null,
            'cred_note' => null,
            'cred_prefix' => null,
            'cred_suffix' => null,
            'cred_counter' => null,
        ],
    ];

It works great with new Model but I get an error if I try to use firstOrCreate()

Error in TinkerWell

Argument 1 passed to Illuminate/Database/Grammar::parameterize() must be of the type array, string given, called in /Users/tina/Sites/bookons-api/vendor/laravel/framework/src/Illuminate/Database/Query/Grammars/Grammar.php on line 866

Error in Horizon (I am trying to make a batch import)

ErrorException: json_decode() expects parameter 1 to be string, array given in /Users/tina/Sites/bookons-api/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php:811

If I comment out the attributes I can use firstOrCreate() without any problem.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 15 (15 by maintainers)

Most upvoted comments

Thank You both @driesvints and @netpok for taking the time to explain 😃 Grateful!

protected $attributes = [
    'invoicing' => '{"calc_due_days_from":"invoice_date","invoice_date_num_days":30,"before_start_num_days":48,"currency_code":"SEK","currency_symbol":"kr","invoice_note":null,"inv_prefix":null,"inv_suffix":null,"inv_counter":null,"cred_note":null,"cred_prefix":null,"cred_suffix":null,"cred_counter":null}',
];

Attributes are the values after they’ve already been casted. You’ll need to either provide the json payload in the attributes array or use something like @netpok suggested.

The attributes array stores the attributes in a database compatible format, I never used it in that way, usually when I needed a similar feature I used the model::creating event to set them if needed.

If you still want to use that you need to use that in the same format as the database (a JSON string), but please note laravel will not merge those fields for you, it will just simply overwrite it with the data you provided.

To achieve what you wanted you should probably use something similar:

static function boot(){
    parent::boot();

    static::creating(function($model){
        $model->invoicing = array_merge([/* defaults */], $model->invoicing);
    });
}