Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

ctrlaltdelme's avatar

Fillable and user_id (Mass Assignment Question)

I'm working on a new project and am still struggling to understand the fillable property and best practices.

I know user_id shouldn't be fillable, but are there times it might need to be?

Can someone explain how mass assignment works and fillable and any general "rules" on what fields to set as fillable? And if possible how not setting one affects how you use Eloquent?

Sorry I feel dumb 😔

0 likes
9 replies
Tray2's avatar

In general you never assign a primary key, you let the db handle that for you with auto increments. When using foreign keys, then you need to make it fillable.

I usually set the guarded to an empty array, and you are safe doing this as long as you validate your requests. If you don't want to unguard it completely, you need to add the foreign key to your fillable array.

1 like
JussiMannisto's avatar

I always set $guarded = ['id'] by default. It's an additional safeguard.

2 likes
Glukinho's avatar

$fillable and $guarded control which model fields can be assigned "blindly" without explicitly enumerating which fields should have which values.

Let's say you want to update a user with these values from request:

$new_values = $request->input('user_data');

// $new_values = [ 'name' => 'John Doe', 'nickname' => 'johndoe2025' ]

Using ->update() fields will not be updated unless you have $fillable = ['name', 'nickname']:

$user = User::find(123);
$user->update($new_values);

Using ->save() you explicitly specify fields and new values, so the model will be updated regardless of $fillable and $guarded:

$user = User::find(123);
$user->name     = 'John Doe';
$user->nickname = 'johndoe2025';
$user->save();

$guarded and $fillable arrays are opposite in their meaning; set $fillable fields you WANT to update implicitly, or set $guarded fields you want to protect from blind updating and update only explicitly, specifying fields and values.

In real life it is used to prohibit mistaken setting important fields (like id) from unvalidated sources. As @Tray2 said, you can protect yourself with form request validation.

Imagine your incoming request has this data:

[
	'user_data' => [
		'id'       => 321,
		'name'     => 'John Doe',
		'nickname' => 'johndoe2025',
	]
]

If you just update your model with this user_data array, you may change your model's id, which of course should never happen. Having $guarded = ['id'] or $fillable = ['name', 'nickname'] saves you from this situation.

1 like
ctrlaltdelme's avatar

This makes more sense and helps clear things up! Thank you! I believe I was doing it right.

For example, I have a Vulnerability Model that has the following

protected $fillable = [
        'title',
        'cve_id',
        'description',
        'severity',
        'affected_product',
        'affected_versions',
        'status',
        'reporter_name',
        'reporter_email',
        'cvss_score',
        'remediation',
        'discovered_at',
        'disclosed_at',
        'resolved_at',
        'references',
        'user_id',
    ];

And going by @Tray2's response, it's okay to set foreign keys as fillable. So user_id here is a foreign key, and is OK to be fillable.

Tray2's avatar

That is correct.

I however, like I stated prefer using validation to determine what should be inserted.

Take this for example:

$validData = $request->validate([
	'title' => 'required',
    'body' => 'required',
]);

Post::create($validData);

The $validData array will only contain the title and the body, so even if I pass in id, it will not get inserted in the table.

Here is another example using a form request class, and foreign keys.

We start with the controller.

public function __invoke(MovieFormRequest $request)
    {
        Movie::create($request->validated());
        return redirect(route('movies.index'));
    }

And the Form request

class MovieFormRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'title' => ['required', 'string'],
            'release_year' => [
                'required',
                'numeric',
                'min_digits:4',
                'max_digits:4',
                'between:1800,'.Carbon::now()->addYear(1)->year,
            ],
            'blurb' => ['required', 'string', new MinWords(3)],
            'runtime' => ['required', 'numeric', 'min_digits:2', 'max_digits:3', 'between:1,999'],
            'actor' => ['required', 'array'],
            'genre_id' => ['required', 'exists:genres,id'],
            'format_id' => ['required', 'exists:format,id'],
        ];
    }
}
3 likes
Glukinho's avatar

Yes, you can have foreign keys in $fillable, it's fine. However, you can set relations explicitly, like this:

$user = User:: findOrFail($data['user_id']);

$vulnerability->user()->associate($user);

In this case you may not have user_id in $fillable.

1 like
ctrlaltdelme's avatar

That's awesome! Thanks for clarifying and explaining. I definitely try to use Form Requests when possible everywhere I can.

What do you think about the fill method? Or do you prefer explicitly setting which fields to validate and pass?

Glukinho's avatar

What do you think about the fill method?

What do you mean?

Snapey's avatar

fill() simply copies an array of key,value pairs into the model.

Nothing more.

Please or to participate in this conversation.