Laravel 9 Roles And Permissions Without Package Example

Hello Artisan,

In this Laravel 9 permission tutorial, I will show you a complete laravel 9 roles and permission tutorial example without packages. We can create this roles and permission system using spatie roles and permission or laratrust roles and permission packages. In this example, I will create a custom user roles and permission example in Laravel 9 application.

I have already made a tutorial on spatielaravel permission and you can read it from the below link. From this laravel 9 dynamic roles and permissions tutorial, You will learn how we can manage user roles and permissions and their respective capabilities easily. For a big laravel application, using this user permissions, we can handle user easily.

That's why I am here to share with you that how we can create a roles and permissions in laravel 9 without package. You have to just follow some few steps to complete this roles and permission tutorial.

So let's start this Laravel roles and permission tutorial in laravel 9 example:

Using Spatie Permission Package

 

Recommended: Laravel Spatie Roles and Permissions Tutorial from Scratch

 

Using Laratrust Permission Package

 

Recommended: Laravel 8 User Roles and Permissions Tutorial using Laratrust

 

Also, you can see the GitHub repository of these roles and permissions tutorial.

 

Git Repository : User Roles & Access Permission Laravel 

 

roles-permissions-laravel

 

Step 1 : Download Laravel Project

To create laravel 9 user roles and permissions without package open up your terminal and create a new Laravel project by typing in the following command

composer create-project --prefer-dist laravel/laravel blog

 

Step 2:  Make Auth

We are going to create laravel user roles and permissions without package so If you are using laravel version 6 then run below command to make auth

composer require laravel/ui --dev
php artisan ui vue --auth
npm install
npm run watch

 

If you are using below laravel version 6 then run below command to make auth

php artisan make:auth

 

Step 3 : Make Model

We need a model to make users' roles and permissions. So let's create our model using the below command to complete laravel 9 roles and permissions without package.

php artisan make:model Permission -m
php artisan make:model Role -m

 

As you may know, -m flag will create a migration file for the model. Now you’ll have two new migration files waiting for you to add new fields. 

 

Step 4 : Edit the migration file

Now update all the migrations like below:

public function up()
    {
       Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email',191)->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
    });
}

 

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

class CreatePermissionsTable extends Migration
{
    
    public function up()
    {
        Schema::create('permissions', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name'); // edit posts
            $table->string('slug'); //edit-posts
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('permissions');
    }
}

 

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

class CreateRolesTable extends Migration
{
    public function up()
    {
        Schema::create('roles', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name'); // edit posts
            $table->string('slug'); //edit-posts
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('roles');
    }
}

 

Step 5 : Adding pivot tables

For this first pivot table, we’ll create a new migration file for the table users_permissions. So run the below command to create

php artisan make:migration create_users_permissions_table --create=users_permissions

 

For this pivot table between users and permissions, our schema should look like

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

class CreateUsersPermissionsTable extends Migration
{
    public function up()
    {
        Schema::create('users_permissions', function (Blueprint $table) {
            $table->unsignedInteger('user_id');
            $table->unsignedInteger('permission_id');

            //FOREIGN KEY CONSTRAINTS
            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
            $table->foreign('permission_id')->references('id')->on('permissions')->onDelete('cascade');
 
            //SETTING THE PRIMARY KEYS
            $table->primary(['user_id','permission_id']);
        });
    }

    public function down()
    {
        Schema::dropIfExists('users_permissions');
    }
}

 

Now let’s create a pivot table for users_roles.

php artisan make:migration create_users_roles_table --create=users_roles

 

The fields inside this table will pretty much the same as in users_permissions table. Our schema for this table will look like:

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

class CreateUsersRolesTable extends Migration
{
    public function up()
    {
        Schema::create('users_roles', function (Blueprint $table) {
            $table->unsignedInteger('user_id');
            $table->unsignedInteger('role_id');

         //FOREIGN KEY CONSTRAINTS
           $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
           $table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade');

         //SETTING THE PRIMARY KEYS
           $table->primary(['user_id','role_id']);
        });
    }

    public function down()
    {
        Schema::dropIfExists('users_roles');
    }
}

 

Under a particular Role, the User may have specific Permission

For example, a user may have permission to post a topic, and an admin may have permission to edit or delete a topic. In this case, let’s set up a new table for roles_permissions to handle this complexity.

 

php artisan make:migration create_roles_permissions_table --create=roles_permissions

 

The Schema will be like this:

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

class CreateRolesPermissionsTable extends Migration
{
    public function up()
    {
        Schema::create('roles_permissions', function (Blueprint $table) {
             $table->unsignedInteger('role_id');
             $table->unsignedInteger('permission_id');

             //FOREIGN KEY CONSTRAINTS
             $table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade');
             $table->foreign('permission_id')->references('id')->on('permissions')->onDelete('cascade');

             //SETTING THE PRIMARY KEYS
             $table->primary(['role_id','permission_id']);
        });
    }

    public function down()
    {
        Schema::dropIfExists('roles_permissions');
    }
}

 

Now run the following command to create a migration

php artisan migrate

 

Step 6 : Setting up the relationships

We’ll start by creating the relationships between roles and the permissions table. In our Role.php, Permision.php.

App/Role.php

public function permissions() {

   return $this->belongsToMany(Permission::class,'roles_permissions');
       
}

public function users() {

   return $this->belongsToMany(User::class,'users_roles');
       
}

 

App/Permission.php

public function roles() {

   return $this->belongsToMany(Role::class,'roles_permissions');
       
}

public function users() {

   return $this->belongsToMany(User::class,'users_permissions');
       
}

 

Step 7 : Creating a Trait

Inside of our app directory, let’s create a new directory and name it as Permissions and create a new file namely HasPermissionsTrait.php. A nice little trait has been setup to handle user relations. Back in our User model, just import this trait and we’re good to go.

app/User.php

namespace App;

use App\Permissions\HasPermissionsTrait;

class User extends Authenticatable
{
    use HasPermissionsTrait; //Import The Trait
}

 

Now open HasPermissionsTrait.php and paste those following code.

App/Permissions/HasPermissionsTrait.php

namespace App\Permissions;

use App\Permission;
use App\Role;

trait HasPermissionsTrait {

   public function givePermissionsTo(... $permissions) {

    $permissions = $this->getAllPermissions($permissions);
    dd($permissions);
    if($permissions === null) {
      return $this;
    }
    $this->permissions()->saveMany($permissions);
    return $this;
  }

  public function withdrawPermissionsTo( ... $permissions ) {

    $permissions = $this->getAllPermissions($permissions);
    $this->permissions()->detach($permissions);
    return $this;

  }

  public function refreshPermissions( ... $permissions ) {

    $this->permissions()->detach();
    return $this->givePermissionsTo($permissions);
  }

  public function hasPermissionTo($permission) {

    return $this->hasPermissionThroughRole($permission) || $this->hasPermission($permission);
  }

  public function hasPermissionThroughRole($permission) {

    foreach ($permission->roles as $role){
      if($this->roles->contains($role)) {
        return true;
      }
    }
    return false;
  }

  public function hasRole( ... $roles ) {

    foreach ($roles as $role) {
      if ($this->roles->contains('slug', $role)) {
        return true;
      }
    }
    return false;
  }

  public function roles() {

    return $this->belongsToMany(Role::class,'users_roles');

  }
  public function permissions() {

    return $this->belongsToMany(Permission::class,'users_permissions');

  }
  protected function hasPermission($permission) {

    return (bool) $this->permissions->where('slug', $permission->slug)->count();
  }

  protected function getAllPermissions(array $permissions) {

    return Permission::whereIn('slug',$permissions)->get();
    
  }

}

 

Here, we’re iterating through the roles and checking by the slug field, if that specific role exists. You can check or debug this by using:

$user = $request->user(); //getting the current logged in user
dd($user->hasRole('admin','editor')); // and so on

 

Step 8 :  Create CustomProvider

We’ll be utilizing Laravel’s “can” directive to check if the User has Permission. and instead of using $user->hasPermissionTo().

we’ll use $user->can() To do so, we need to create a new PermissionsServiceProvider for authorization

php artisan make:provider PermissionsServiceProvider

 

Register your service provider and head over to the boot method to provide us a Gateway to use can() method.

namespace App\Providers;

use App\Permission;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\ServiceProvider;

class PermissionsServiceProvider extends ServiceProvider
{
   
    public function register()
    {
        //
    }

    public function boot()
    {
        try {
            Permission::get()->map(function ($permission) {
                Gate::define($permission->slug, function ($user) use ($permission) {
                    return $user->hasPermissionTo($permission);
                });
            });
        } catch (\Exception $e) {
            report($e);
            return false;
        }

        //Blade directives
        Blade::directive('role', function ($role) {
             return "if(auth()->check() && auth()->user()->hasRole({$role})) :"; //return this if statement inside php tag
        });

        Blade::directive('endrole', function ($role) {
             return "endif;"; //return this endif statement inside php tag
        });

    }
}

 

now we have to register our PermissionsServiceProvider. Open this following file add this in providers array.

config\app.php

 'providers' => [

        App\Providers\PermissionsServiceProvider::class,
    
 ],

 

You can learn more about Laravel’s Gate facade at Laravel’s documentation. You can test it out as:

dd($user->can('permission-slug'));

 

Read also :  Laravel 9 Authorization using Gates

 

Step 9 : Add Dummy Data To Check

For creating roles and permissions tutorials, we need dummy data to check our user access. To create it paste the following code into this following slug.

Route::get('/roles', 'PermissionController@Permission');

 

App\Http\Controllers\PermissionController.php

namespace App\Http\Controllers;

use App\Permission;
use App\Role;
use App\User;
use Illuminate\Http\Request;

class PermissionController extends Controller
{   

    public function Permission()
    {   
    	$dev_permission = Permission::where('slug','create-tasks')->first();
		$manager_permission = Permission::where('slug', 'edit-users')->first();

		//RoleTableSeeder.php
		$dev_role = new Role();
		$dev_role->slug = 'developer';
		$dev_role->name = 'Front-end Developer';
		$dev_role->save();
		$dev_role->permissions()->attach($dev_permission);

		$manager_role = new Role();
		$manager_role->slug = 'manager';
		$manager_role->name = 'Assistant Manager';
		$manager_role->save();
		$manager_role->permissions()->attach($manager_permission);

		$dev_role = Role::where('slug','developer')->first();
		$manager_role = Role::where('slug', 'manager')->first();

		$createTasks = new Permission();
		$createTasks->slug = 'create-tasks';
		$createTasks->name = 'Create Tasks';
		$createTasks->save();
		$createTasks->roles()->attach($dev_role);

		$editUsers = new Permission();
		$editUsers->slug = 'edit-users';
		$editUsers->name = 'Edit Users';
		$editUsers->save();
		$editUsers->roles()->attach($manager_role);

		$dev_role = Role::where('slug','developer')->first();
		$manager_role = Role::where('slug', 'manager')->first();
		$dev_perm = Permission::where('slug','create-tasks')->first();
		$manager_perm = Permission::where('slug','edit-users')->first();

		$developer = new User();
		$developer->name = 'Mahedi Hasan';
		$developer->email = 'mahedi@gmail.com';
		$developer->password = bcrypt('secrettt');
		$developer->save();
		$developer->roles()->attach($dev_role);
		$developer->permissions()->attach($dev_perm);

		$manager = new User();
		$manager->name = 'Hafizul Islam';
		$manager->email = 'hafiz@gmail.com';
		$manager->password = bcrypt('secrettt');
		$manager->save();
		$manager->roles()->attach($manager_role);
		$manager->permissions()->attach($manager_perm);

		
		return redirect()->back();
    }
}

 

Now go to this URL and hit enter on your keyboard. Then you will see some dummy data in the following tables. To test this out in your routes files, we can die and dump on:

$user = $request->user();
dd($user->hasRole('developer')); //will return true, if user has role
dd($user->givePermissionsTo('create-tasks'));// will return permission, if not null
dd($user->can('create-tasks')); // will return true, if user has permission

 

Inside of our view files, we can use it like:

@role('developer')

 Hello developer

@endrole

 

This means only those users can see it whose role is the developer. Now you can use many roles as you want.

 

Step 10 : Setup the Middleware

 

Read also : How to Create Middleware with Parameters in Laravel 9

 

In order to protect our routes, we can set up the middleware to do so.

php artisan make:middleware RoleMiddleware

 

Add the middleware into your kernel & setup the handle method as follows

App\Http\Middleware\RoleMiddleware.php

namespace App\Http\Middleware;

use Closure;

class RoleMiddleware
{

    public function handle($request, Closure $next, $role, $permission = null)
    {
        if(!$request->user()->hasRole($role)) {

             abort(404);

        }

        if($permission !== null && !$request->user()->can($permission)) {

              abort(404);
        }

        return $next($request);

    }
}

 

Now we have to register this RoleMiddleware. So add the following code to register it.

App\Http\Kernel.php

protected $routeMiddleware = [
    .
    .
    'role' => \App\Http\Middleware\RoleMiddleware::class,
];

 

Right now in our routes, we can do something like this

Route::group(['middleware' => 'role:developer'], function() {

   Route::get('/admin', function() {

      return 'Welcome Admin';
      
   });

});

 

Now you can use your controller like below to give user permission and access.

public function __construct()
{
   $this->middleware('auth'); 
}


public function store(Request $request)
{
    if ($request->user()->can('create-tasks')) {
        //Code goes here
    }
}

public function destroy(Request $request, $id)
{   
    if ($request->user()->can('delete-tasks')) {
      //Code goes here
    }

}

 

Read also : Understanding Constructor and Method Dependency Injection in Laravel

 

Now only those users can access this route whose role is the developer. Hope you will understand the total procedure. Hope it will help you.

 

#laravel #laravel-9x #laravel-permission