DEV Community

Zubair Mohsin
Zubair Mohsin

Posted on • Edited on

Understanding Mass Assignment in Laravel Eloquent ORM

Laravel Eloquent ORM provides a simple API to work with database. It is an implementation of Active Record pattern. Each database table is mapped to a Model class which is used for interacting with that table.

Mass Assignment

Let's break down this word.

  • Mass means large number
  • Assignment means the assignment operator in programming

In order to make developers life easier, Eloquent ORM offers Mass Assignment functionality which helps them assign(insert) large number of input to database.

Life, without Mass Assignment 😩

Let's consider that there is a form with 10 fields of user information.

    <form method="POST" action="/signup">
        <input type="text" name="name" />
        <input type="text" name="user_name" />
        <input type="text" name="password" />
        <input type="text" name="address" />
        <input type="text" name="city" />
        ...
        ...
        ...
        <button type="submit">Signup</button>
    </form>
Enter fullscreen mode Exit fullscreen mode

and our Controller's store method looks like:

public function store(Request $request)
{
    //perform validation

    $user = new User;

    $user->name = $request->get('name');
    $user->user_name = $request->get('user_name');
    $user->password = bcrypt($request->get('password'));
    $user->address = $request->get('address');
    $user->city = $request->get('city');

    //....

    $user->save();
}
Enter fullscreen mode Exit fullscreen mode

We are assigning each input manually to our model and then saving to database.

What if there was a way to insert all the coming input without the manual assignment?

Life, with Mass Assignment 🥳

Laravel Eloquent ORM provides a create method which helps you save all the input with single line.

public function store(UserFormRequest $request)
{
    //validation performed in the UserFormRequest

    $user = User::create($request->validated());
} 
Enter fullscreen mode Exit fullscreen mode

How cool is that, right? 🤓 With single line we are saving all input to database. In future, if we add more input fields in our HTML form, we don't need to worry about our saving to database part.

Input fields name attribute must match the column name in our database.

Potential Vulnerability

For the sake of understanding, let's consider that we have an is_admin column on users table with true / false value.

A malicious user can inject their own HTML, a hidden input as below:

    <form method="POST" action="/signup">

        <input type="hidden" name="is_admin" value="1" />

        <input type="text" name="name" />
        <input type="text" name="user_name" />
        <input type="text" name="password" />
        <input type="text" name="address" />
        <input type="text" name="city" />
        ...
        ...
        ...
        <button type="submit">Signup</button>
    </form>
Enter fullscreen mode Exit fullscreen mode

With Mass Assignment, is_admin will be assigned true and the user will have Admin rights on website, which we don't want 😡

Avoiding the vulnerability

There are two ways to handle this.

  • We can specify (whitelist) which columns can be mass assigned.

Laravel Eloquent provides an easy way to achieve this. In your model class, add $fillable property and specify names of columns in the array like below:

class User extends Model
{
    protected $fillable = [
        'name',
        'user_name',
        'password',
        'address',
        'city'
        ...
    ];
}
Enter fullscreen mode Exit fullscreen mode

Any input field other than these passed to create() method will throw MassAssignmentException.

  • We can specify ( blacklist ) which columns cannot be mass assigned.

You can achieve this by adding $guarded property in model class:

class User extends Model
{
    protected $guarded = ['is_admin'];
}
Enter fullscreen mode Exit fullscreen mode

All columns other than is_admin will now be mass assignable.

You can either choose $fillable or $guarded but not both.

So there you have it. Happy Mass Assigning with Laravel Eloquent 🥳 👨🏽‍💻

Top comments (7)

Collapse
 
mazentouati profile image
Mazen Touati • Edited

Great breakdown I would like to add these extra tips:

  • If all of your table fields are fillable you should consider using protected $guarded = []

  • You can use $request->only(array $fields) to pick only certain columns.

  • Assuming you're using Laravel form request (learn more here) you can simply use $request->validated() to get the validated data.

PS: for tip 2 & 3 you probably should opt for protected $guarded = []

Collapse
 
zubairmohsin33 profile image
Zubair Mohsin

Hey thank you so much for sharing these tips. There are multiple ways to achieve the same objective. To get better understanding, I kept the options minimal.

Collapse
 
roelofjanelsinga profile image
Roelof Jan Elsinga

I've used Laravel professionally for 5 years and even though I assumed this is how it worked, I learned something new here! Great post, thank you for this!

Collapse
 
zubairmohsin33 profile image
Zubair Mohsin

Awesome :) Glad you liked it.

Collapse
 
wakeupneo47 profile image
Neo

Great job , great explanation.

Collapse
 
victorallen22 profile image
Victor Allen • Edited

Very well put...you got me sorted! Thanks for sharing 👍

Collapse
 
ypaatil profile image
ypaatil

Great job. You are cleared my concept.