3

I am attempting to save data using Laravel's firstOrNew method, which is producing a MassAssignmentException error.

I don't understand why the firstOrNew method should trigger a mass assignment exception since it is merely looking at the database, not attempting to save anything. From the docs:

The firstOrNew method, like firstOrCreate will attempt to locate a record in the database matching the given attributes. However, if a model is not found, a new model instance will be returned. Note that the model returned by firstOrNew has not yet been persisted to the database.

Why then, should my code be throwing a MAE:

$tour = Tour::firstOrNew(array('id' => $this->request['id']));

As I understand, all that the above code is saying is basically "look and see if there is a row in the database with that ID, if so then return the row, if not, then create a new object with the relevant properties". It isn't saving any data so what is the problem?

Note that the error only occurs if the row doesn't exist.

Inigo
  • 8,110
  • 18
  • 62
  • 110
  • 1
    You're trying to create an object by using an ID. This might be fine in your case, but usually ID's auto increment, so you wouldn't want to create a specific one. I'd firstOrNew an object by another attribute – if the name exists grab it, otherwise create one with that name. Also you could think about looking into findOrFail which sounds more suitable https://laravel.com/docs/5.3/eloquent#retrieving-single-models – does an object with this ID exist? If so get, it, otherwise throw an exception – Djave Nov 30 '16 at 12:32
  • Thanks, although `findOrFail` probably isn't suitable since according to the docs it sends a 404 automatically, which I don't want. ID is indeed auto incrementing and is the only column guaranteed to be unique which is I why I wanted to use it. Not sure how to do this then.... – Inigo Nov 30 '16 at 12:42
  • Yeah I hear you. Alexey Mezenin's answer covers all the reasons why its failing. If you wanted an easy ride I'm pretty sure `$tour = Tour::find($this->request['id']); if( ! $tour) $tour = new Tour;` would be a really pain free way of getting to the same place. Good luck! – Djave Nov 30 '16 at 12:51
  • Yes, that would be a quick solution. I think I'm going to also implement a randomly generated UID column in my tables in addition to auto-incementing ID though, as I can see this problem cropping up again. Thanks for your help. – Inigo Nov 30 '16 at 12:59

2 Answers2

4

firstOrNew() uses newInstance() which creates new model using constructor. Constructor uses fill() method which is Mass Assigment. That's why you're getting an exception.

So, you need to set $fillable array to use firstOrNew()

Alexey Mezenin
  • 158,981
  • 26
  • 290
  • 279
  • 1
    Ah, OK. *Constructor uses fill() method which is Mass Assigment.* That's the key there I guess. – Inigo Nov 30 '16 at 12:36
1

MassAssignmentException occurs when you do not have fillable or guarded fields defined in the Model

$guarded = [];
$fillable = ['explicitlydefinedfield'];

PS: you have copied the text which says exactly what is happening... it did not find the record and is trying to create a new one.

firstOrNew ... NEW

Aleksei Maide
  • 1,845
  • 1
  • 21
  • 23
  • Thanks, I am aware of setting the `guarded` and `fillable` attributes, I just don't understand why they are necessary when data is not being 'persisted' (ie. saved) to the database – Inigo Nov 30 '16 at 12:33
  • I think the next answer addressed that better :) – Aleksei Maide Nov 30 '16 at 12:35