I'm making a course on Laravel Sanctum: MasteringAuth.com

Laravel mass assignment and fillable vs guarded

Constantin Druc ยท 11 Aug, 2021

[00:07] Second, if you have some attributes that shouldn't be allowed to be changed in this particular controller action, well you're kind of screwed because...

[00:09] So mass assignment is when you use an array to create or update an eloquent model. Instead of setting the attributes one by one and then save, you do something like this. You say talk update, and then you pass an array of attributes: title request title and description.

[00:34] So here we are setting the attributes one by one and then save, while here we are using mass assignment - we pass in an array that updates multiple model attributes at once.

[00:46] Now, sometimes I hear people say mass assignment is bad and that it should be avoided. However, the problem isn't mass assignment; mass assignment is not bad. What is bad, is doing mass assignment without knowing exactly what is in the array you are passing in.

[00:58] One example I've seen many people do, and I am guilty of this myself; I used to do it all the time, is passing all the request parameters to the create or update method. Something like this: Talk::update(request()->all()). The reason I used to do it like this is, well, it's shorter. The controller method itself looks much smaller now, which is nice.

[01:26] However, code that is shorter isn't necessarily better. This is actually a horrible thing to do, and here's why. First of all, it's hard for anyone to figure out what is expected to be in this array. Looking at it, I have no idea what attributes will get updated. Will this array contain all the attributes? Some of them? Which ones? I have no idea.

[01:54] Second, if you have some attributes that shouldn't be allowed to be changed in this particular controller action, well you're kind of screwed because people can send those attributes whether you like it or not - after all, you are accepting all of them. So again, the problem is not mass assignment, the problem is doing it without specifying the exact attributes you want to change.

[02:20] One solution, if you still want to keep things short, assuming your request parameters match your model attributes, is to call the request method and pass it an array with the keys you want to retrieve: in our case, title and description. This will filter out any other parameters except the ones you specified in the array you passed in. It's basically a fancier way of doing this.

[02:47] Another solution is to only allow validated parameters. Here the validate method will return an array with all the validated fields, so we can do something like this, and then talk update validated fields. However, doing it in this way assumes that the validate method contains every field we want to update, even the optional ones like the description field. So in order to make it work we'd need to add description to the array as well. But then again, if this validation would be made with the help of a form request object, we'd still have to jump inside that form class to check what fields we're dealing with, so the best way to do it, in my opinion, is to be explicit and write down exactly what attributes you want to change.

[03:42] Now, there's a constant argument whether to use fillable or guarded when protecting against mass assigning certain attributes. The fillable property allows you to specify what fields can be mass assigned, while using guarded does the reverse thing; it specifies what fields cannot be mass assigned. So say your model has three attributes: title, description, and approved_at, and you want to protect the approve_at attribute from being mass assigned.

[04:20] You can do this either by setting fillable to title and description, and what this basically says is, these are the only attributes that can be mass assigned. So approved_at, since it's missing from this fillable list, is now protected against mass assignment.

[04:38] The second way to do it is to set the guarded property to approved_at - and this says, all the attributes can be mass assigned, except the approved_at attribute.

[04:53] Now, how to choose between these two? I've seen people using fillable as a place to hold all the available fields, and I've done this before. In order to know what attributes this model has you would just open the model and look at the fillable property. However, this means you always have to keep this list of attributes in sync with the database columns. So whenever you add another column to the table you need to make sure you add it to the fillable array as well - and that's just...super annoying.

[05:21] Back to the guarded version, things are a bit better because you only need to list the attributes you don't want to be mass assigned. So you no longer have to sync the attributes back and forth with your database columns. But what do I personally do in my projects, well, I set guarded to an empty array. I don't protect against mass assignment. What I do do is I never allow uncontrolled mass assignment. Every time I create or update records I make sure to specify the attributes I want to change. This way everyone can see exactly what attributes are getting filled in. So what you need to remember is, it doesn't really matter whether you use fillable or guarded, what is super important is to do controlled, specific, mass assignment so I know exactly what attributes are changed.