Custom Laravel Roles and Permissions

In this article you will be reading about how you can build your own custom logic for the roles and permissions in Laravel or any other framework.
Although Laravel has Gates and Policies and have some external packages which you can implement for roles and permissions but there are no gates and policies or packages in other frameworks, so there we need to implement roles and permission customly and that is what exactly this article is about.

Why do you need to implement Roles and Permissions ?

Modern web application requires a complete ecosystem and without the roles and permission that cannot be possible. If you are building any management system, woocommerce application, blog website or any other web app you will need to restrict some user and allow some user to perform a specific actions.

This article will be covering how you can restrict some users and allow to perform some actions like read, delete, view and update.

Note that this article does not focus on the frontend that how your permission page will look like but rather the logic that how you can implement the roles and permissions. You can use whatever you like for the frontend and whatever the design you want.

Laravel Roles

Implementation:

Note that these permissions are module based means that we will have to define for which module the particular user role has which permissions.

Let’s get started from very scratch so install a fresh copy of laravel.
If you don’t know how to install laravel you can read How to install Laravel

If you want to implement Permissions through Gates then read Implementing Laravel Permissions through Gates.

we Will be creating a role, module, and permissions migrations with their respective models.

roles, modules and permissions migrations

<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateRolesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('roles',
            function (Blueprint $table) {
                $table -> id();
                $table -> string('role_name') -> unique();
                $table -> timestamps();
            }
        );
    }
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('roles');
    }
}
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateModulesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create(
            'modules',
            function (Blueprint $table) {
                $table -> id();
                $table -> string('module_name');
                $table -> timestamps();
            }
        );
    }
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('modules');
    }
}
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePermissionsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('permissions',
            function (Blueprint $table) {
                $table -> id();
                $table -> unsignedBigInteger('module_id');
                $table -> unsignedBigInteger('role_id');
                $table -> boolean('view') -> default(0);
                $table -> boolean('add') -> default(0);
                $table -> boolean('edit') -> default(0);
                $table -> boolean('delete') -> default(0);
                // foriegn key from modules table
                $table -> foreign('module_id') -> references('id')
                    -> on('modules');
                // foriegn key from roles table
                $table -> foreign('role_id') -> references('id')
                    -> on('roles');
                $table -> timestamps();
            }
        );
    }
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('permissions');
    }
}

Role, Module and Permission Models

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
    protected $fillable = ['role_name'];
    public function users()
    {
        return $this->hasMany('App\User', 'user_role_id');
    }
    public function permissions()
    {
        return $this->hasMany('App\Permission');
    }
}
//Module.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Module extends Model
{
    protected $fillable = ['module_name'];
    public function permissions()
    {
        return $this->hasMany('App\Permission');
    }
}
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Permission extends Model
{
    protected $fillable =
    [
        'module_id',
        'role_id',
        'view',
        'add',
        'edit',
        'delete'
    ];
    public function module()
    {
        return $this->belongsTo('App\Module', 'module_id');
    }
    public function role()
    {
        return $this->belongsTo('App\Role', 'role_id');
    }
}

By Default there comes a user model and migration so we will edit and add a column to users migration user_role_id which will be a foreign key to the roles table.

<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create(
            'users',
            function (Blueprint $table) {
                $table->id();
                $table->string('name');
                $table->string('email')->unique();
                $table -> unsignedBigInteger('usr_role_id');
                $table->timestamp('email_verified_at')->nullable();
                $table->string('password');
                $table->rememberToken();
                $table->timestamps();
                // usr_role_id foreign key reference to roles
                $table -> foreign('usr_role_id') -> references('id')-> on('roles');
            }
        );
    }
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
}
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;
    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'name',
        'email',
        'usr_role_id',
        'password',
    ];
    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];
    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}

Now we can create by some seeder files in order to we have some by default roles and permissions for the Super Admin.

create Seeder for Role, Permissions, Model and User model by the following commands.

php artisan make:seeder ModuleSeeder
php artisan make:seeder RoleSeeder
php artisan make:seeder UserSeeder
php artisan make:seeder PermissionSeeder

These commands will create four seeder files.

You can write some code to dump some data into database like for example if your application has blog module then write the following code in your ModuleSeeder file in the run function which comes by default with the seeder file.

DB::table('modules') -> insert(
   [
       'module_name' => 'Blog',
       'module_type' => '1',
       'created_at' => now(),
       'updated_at' => now()
   ]
);

This will create blog Entry in the table modules after we will run the migration with seed.

Similarly write the following code into the other module files as well.

RoleSeeder.php

DB::table('roles') -> insert(
    [
        'role_name' => 'Admin',
        'created_at' => now(),
        'updated_at' => now()
    ]
);

PermissionSeeder.php

DB::table('permissions') -> insert(
    [
        'module_id' => 1,
        'role_id' => 1,
        'view' => 1,
        'add' => 1,
        'edit' => 1,
        'delete' => 1,
        'created_at' => now(),
        'updated_at' => now()
    ]
);

UserSeeder.php

Don’t forget to add the following lines to userseeder file.

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;

DB::table('users') -> insert(
    [
        'usr_name' => 'admin',
        'email' => '[email protected]' //you can write any admin email here
        'usr_role_id' => 1,
        'usr_password' => Hash::make('password'), //you can create any password
        'created_at' => now(),
        'updated_at' => now()
    ]
);

Now will include all these seeder in the main database seeder file like,

<?php
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        $this->call(ModuleSeeder::class);
        $this->call(RoleSeeder::class);
        $this->call(PermissionSeeder::class);
        $this->call(UserSeeder::class);
    }
}

Now we want to migrate our migration files in order to we have tables in database so will migrate the files and will provide the seed flag to dump the data that we created in the seeder files.

In order to execute the above statement we will run the following command,

php artisan migrate --seed

This will create database tables and will dump the data.

Now that you want to make change in the application and want to remigrate then it will not affect the initial data that dumped for the admin.

If you want a fresh migration with the particular data then execute the following command.

php artisan migrate:fresh --seed

Now That we have everything setup for the data and database.
It’s totally depends on you how you make the frontend views for the permissions and roles (note that this article doesn’t focuses on how to make the frontend but how to implement the logic).

You can create whatever views you want I will post a screenshot that how an ideal situation will be like for this type of problem.

Note the below image is from a real working application you will have to create similar like this one.

Screenshot 2022 01 04 at 2.00.10 PM

Now that you will have every crud for the role and permission, then you can give a role to the user and that user will have that role with the particular permissions.

In Order to fetch these permissions, either get it statically via auth id or you can store it in the session and refresh the session each time you perform an action.
On the login save the permissions in the session and create a helper class and call in every controller method in order to refresh the permissions and apply it whatever the way you want.

You can also get the permissions in the middleware it’s totally depends on you how you manage these permissions.
If you have any question post it in the comment and will be answered likewisely.

Follow us TwitterFacebookLinkedIn

Open Source Listing

Previous Post
Next Post

Comments

Laravel Permissions through Gates - Open Source Listing

[…] Laravel How to Install Laravel ?Infinite Scroll in Laravel with LivewireCustom Laravel Roles and Permissions Open Source Listing What is AnsibleWhat is Web 3.0What is DockerIntroduction to GitHub for […]

Avatar for Sangha
Sangha

worth reading.
Just solved my Problem

Laravel Permissions through Policies - Open Source Listing

[…] Laravel How to Install Laravel ?Infinite Scroll in Laravel with LivewireCustom Laravel Roles and Permissions Open Source Listing What is AnsibleWhat is Web 3.0What is DockerIntroduction to GitHub for […]

Avatar for is gate io reliable
is gate io reliable

I agree with your point of view, your article has given me a lot of help and benefited me a lot. Thanks. Hope you continue to write such excellent articles.

Leave a Reply