Creating A User Role System In Laravel

As I continue learning Laravel, often I find the need to create basic features for my sample applications. A common feature is the ability to assign users to a role. While role management packages for this feature already exist, I decided to build one myself and use it as a learning experience.

I am going to assume you already have Laravel 5.4 or greater installed along with Laravel Authentication setup and a semi-working knowledge of Laravel.

What needs to be done?

We need the ability to determine if a user is in a role. A user can have many roles and a role can have many users; also known as a many-to-many relationship. Laravel provides the tools and documentation to easily create a many-to-many relationship between models.

In Laravel a many-to-many relation can be created between two models by referencing each other through belongsToMany() relations and a pivot table which I will explain below.

I will not be covering the user interface to manage roles with this tutorial. The users and roles can easily be managed via Tinker for the scope of this tutorial.

Creating the Role model (and migration)

Let’s create the Role model along with a migration.

$ php artisan make:model Role --migration

Open up the migration {timestamp}_create_roles_table.php file within the database/migrations directory. Update the up() method with the code below.

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateRolesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('roles', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name')->unique();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('roles');
    }
}

The schema for a Role simple; An ID, unique name, and time stamps. Now run the migration.

$ php artisan migrate

Update the Role model to create a relation with the User model. Open the app/Role.php model file and add the following.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Role extends Model
{
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = ['name'];

    /**
     * App\User relationship
     *
     * @return Illuminate\Database\Eloquent\Relations\BelongsToMany
     */
    public function users()
    {
        return $this->belongsToMany(User::class);
    }
}

Add name field to the $fillable array variable or else name values will not be saved to the role. The users() method is a relation definition that will return a BelongsToMany object.

Open Tinker to save a role to the database.

$ php artisan tinker
$ App\Role::create(['name' => 'admin']);

Ensure the role was created by displaying all roles.

$ App\Role::all();

Adding a Role relationship to the User

Now that our Role model has a relationship to the User model, adjust the User model to reference the Role model.

Open the app/User.php model and add the roles() method like below.

<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];

    /**
     * App\Role relation.
     *
     * @return Illuminate\Database\Eloquent\Relations\BelongsToMany
     */
    public function roles()
    {
        return $this->belongsToMany(Role::class);
    }
}

The User model now has a relation to the Role model. Since the relation is going to be many-to-many, a pivot table will be needed to store the relation.

Creating a pivot table

Whenever a many-to-many relationship is needed between two database tables, an intermediate table known as a “pivot table” will be needed to manage the relation. Create a migration script to create the pivot table.

$ php artisan make:migration create_role_user_pivot_table --create=role_user

Please note that Laravel uses a naming convention when referencing relations between models. The naming convention is alphabetical order, singular, and separated by an underscore; In this case role_user will let Laravel know the table is a pivot for the role and user table. The role_id column on role_user will reference id on the role table. More information can be found in the Defining Relationships section of the Laravel documentation.

Open up the new migration file within the database/migrations/{timestamp}_create_role_user_table.php and modify the up() method like the following.

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateRoleUserTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('role_user', function (Blueprint $table) {
            $table->integer('role_id');
            $table->integer('user_id');
            $table->primary(['role_id', 'user_id']);
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('role_user');
    }
}

Simple enough, right? The role_id and user_id columns were both set to primary which will prevent a duplicate relation from being defined.

With the pivot table migration defined, run the migration.

$ php artisan migrate

Adding a user to a role

Now that our model relations and pivot table is setup, open Tinker and test it out.

$ php artisan tinker

Within Tinker, grab some data as variables.

$ $user = factory(App\User)->create();
$ $role = App\Role::first();

Create the association with the attach() method.

$ $user->roles()->attach($role);

Dump $user->roles to see the relation to the user.

$ $user->roles

You can find more information for adding and removing relationships within the Inserting & Updating Related Models section of the Laravel documentation.

A lot more could be done with the User to Role relation; Adding helper methods to the user and creating a middleware to protect actions by a user’s role are two that come to mind. I will save both of those for the next post.

Author: rfmeier

Product developer at WPEngine and runner from Schaumburg, Illinois. I work with php and Python; Spending most of my time digging into source code to see how it all works.