loading...

Understanding Mass Assignment in Laravel Eloquent ORM

zubairmohsin33 profile image Zubair Mohsin ・2 min read

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>

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();
}

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(Request $request)
{
    //perform validation

    $user = User::create($request->all());
} 

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>

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'
        ...
    ];
}

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'];
}

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 🥳 👨🏽‍💻

Posted on by:

Discussion

pic
Editor guide
 

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 = []

 

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.

 

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!

 

Awesome :) Glad you liked it.