Laravel User Roles and Permissions

 Roles and permissions are an important part of many web applications. In this tutorial we will see how we can implement user roles and permissions system in laravel. We will see it from scratch. We won't use /laravel-permission package for doing it. But you can use /laravel-permission to create this roles and permissions system in laravel 7.

It is important to add laravel user roles and permissions mechanism in our large scale application to give the permit to user to specific task. We will see from scratch laravel 7/6 user roles and permissions tutorial. 

If you don't know how to create laravel 7/6 roles and permissions, then you are a right place. I will teach you from scratch laravel roles and permissions.But in this tutorial we will do user roles and permissions in laravel using our own custom code. So let's start how to implement & setup roles and permissions in Laravel.


Step 1 : Download Laravel Project

Open up your terminal and create a new Laravel project by typing in the following command

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

 

Step 2:  Make Auth

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
PHP

 

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

php artisan make:auth
PHP

 

Step 3 : Make Model

We need model to make users roles and permissions. So let's create our model using below command.

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

 

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

 

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

 

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');
    }
}
PHP

 

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');
    }
}
PHP

 

Step 5 : Adding pivot tables

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

php artisan make:migration create_users_permissions_table --create=users_permissions
PHP

 

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');
    }
}
PHP

 

Now let’s create a pivot table for users_roles.

php artisan make:migration create_users_roles_table --create=users_roles
PHP

 

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');
    }
}
PHP

 

Under a particular Role, User may have specific Permission

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

 

php artisan make:migration create_roles_permissions_table --create=roles_permissions
PHP

 

The Schema will be like:

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');
    }
}
PHP

 

Now run following command to create migration

php artisan migrate
PHP

 

Step 6 : Setting up the relationships

We’ll start by creating the relationships between roles and 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');
       
}
PHP

 

App/Permission.php

public function roles() {

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

public function users() {

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

 

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
}
PHP

 

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

}
PHP

 

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
PHP

 

Step 8 :  Create CustomProvider

We’ll be utilizing the Laravel’s “can” directive to check if the User have 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
PHP

 

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

    }
}
PHP

 

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

config\app.php

 'providers' => [

        App\Providers\PermissionsServiceProvider::class,
    
 ],

PHP

 

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

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

 

Step 9 : Add Dummy Data To Check

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

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

 

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 = 'Darshan Gundaniya';
		$developer->email = 'darshan@gmail.com';
		$developer->password = bcrypt('secrettt');
		$developer->save();
		$developer->roles()->attach($dev_role);
		$developer->permissions()->attach($dev_perm);

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

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


After run blow route http://laravel7x.local/roles

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


Then you can login with blow account And Check role on Database

Email: darshan@gmail.com / darshanpatel@gmail.com

Pass: secrettt


Now goto this url and hit enter on your keyboard. Then you will see some dummy data to those 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
PHP

 

App\Http\Controllers\HomeController.php

public function test(Request $request)
{
$user = $request->user();
//will return true, if user has role
dd($user->hasRole('developer'));

// will return permission, if not null
dd($user->givePermissionsTo('create-tasks'));

// will return true, if user has permission
dd($user->can('create-tasks'));
exit;
}


Route::get('/test', 'HomeController@test');


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

@role('developer')

 Hello developer

@endrole
PHP

 

App\Http\Controllers\HomeController.php

public function testView(Request $request)
{
return view('testDeveloper');
}


resources\views\testDeveloper.blade.php

<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Dashboard') }}</div>

<div class="card-body">

@role('developer')

Hello developer

@endrole

</div>
</div>
</div>
</div>
</div>


Route::get('/test-view', 'HomeController@testView');


This means only those user can see it whose role are developer. Now you can use many roles as you want.

 

Step 10 : Setup the Middleware

 

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

php artisan make:middleware AuthMiddleware
PHP

 

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

App\Http\Middleware\RoleMiddleware.php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Route;

class AuthMiddleware
{

    public function handle($request, Closure $next)
    {
        // $currentName = Route::Route::getFacadeRoot()->current()->getName(); // use Laravel from v5.1
        $currentName = Route::currentRouteName(); // use laravel Laravel v6.x...7.x
        $user = $request->user();          if(!$user->can($currentName)) {
abort(404); } return $next($request); } }
PHP

 

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

App\Http\Kernel.php

protected $routeMiddleware = [
    .
    .
    'auth_users' => \App\Http\Middleware\AuthMiddleware::class,
];
PHP

 

Right now in our routes, we can do something like this 
Note: Route Name  set as the same database permissions table slug fields

Route::group(['middleware' => ['auth_users']], function() {
    // tasks
   Route::get('/tasks/store', 'HomeController@store')->name('create-tasks');    Route::get('/tasks/edit', 'HomeController@edit')->name('create-tasks');
   Route::get('/tasks/destory/{id}', 'HomeController@destory')->name('create-tasks');
});
PHP

 

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 
        echo"Yes-you-can-create-tasks";exit;
    }
    echo"No-Permissions-create-tasks";exit;
}

public function destroy(Request $request, $id)
{   
    if ($request->user()->can('delete-tasks')) {
      //Code goes here
      echo"Yes-you-can-delete-tasks";exit;
    }
    echo"No-Permissions-delete-tasks";exit;
}
PHP

 

Route::get('/store', 'HomeController@store');
Route::get('/destory/{id}', 'HomeController@destory');


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

Popular posts from this blog

Yii Framework In Update Time View Image In Form

Add a new column to existing table in a migration

Ajax Toggle(on/off) on-click Update