Laravel Spatie Roles and Permissions Tutorial from Scratch

Hello Artisan,

In this brand new spatie laravel permission tutorial, I am going to show you the uses of spatie laravel roles and permissions tutorial. I will start from scratch so that you can understand the source code easily. I will explain in this example, simply step by step laravel 8 spatie/laravel-permission and also step by step explain laravel 8 acl tutorial.

You know that the Spatie role permission composer package provides a way to create ACL in laravel 8. It also provide how to assign a role to a user very easily, How to assign permission to a user and how to assign permission assign to roles This package allows you to manage user permissions and roles in a database.

Before I have created two tutorials about laravel roles and permission. One was laravel roles and permission without packages and another was Laravel role role and permission tutorial step by step using laratrus permission package.

But in this example, I will use spatie roles and permissions package to control user access in Laravel application. Before started this tutorial, please read below article, which helps you to understand the concept of how spatie works.

 

Recommended : Laravel 8 User Roles and Permissions Tutorial using Laratrust Package

 

Recommended : User Roles and Permissions Tutorial in Laravel Without Packages

 

Let's start our spatie roles and permission tutorial.

 

Step 1: Download Laravel 

I'm going from scratch so, If you haven't installed laravel in your system then you can run bellow command and get fresh Laravel project.

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

 

Step 2: Install Spatie Packages

Now we need to install Spatie package for ACL, that way we can use it's a method. Also, we will install a form collection package. So Open your terminal and run below command.

composer require spatie/laravel-permission

//and then

composer require laravelcollective/html

 

Now open config/app.php file and add service provider and alias.

config/app.php

'providers' => [

	....

	Spatie\Permission\PermissionServiceProvider::class,
],

 

We can also custom changes on the Spatie package, so if you also want to changes then you can fire the below command and get the config file in config/permission.php and migration files.

php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"

 

Now run migrate command.

php artisan migrate

 

Recommended : Laravel Passport Scope Tutorial | API Permissions Using Passport Scope

 

Step 3: Create Product Model and Migration

In this step we need to create three migration for products table as using bellow command:

php artisan make:model Product -m

 

Update product migration like

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


class CreateProductsTable extends Migration
{
    public function up()
    {
        Schema::create('products', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->text('detail');
            $table->timestamps();
        });
    }

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

 

Now run the migration:

php artisan migrate

 

Step 4: Update Models

In this step, we have to create a model for the User and Product table, so if you get a fresh project then you have a User Model have so just replaced the code and others you should create.

app/Models/User.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 Spatie\Permission\Traits\HasRoles;
  
class User extends Authenticatable
{
    use HasFactory, Notifiable, HasRoles;
}

 

Update product model like

app/Models/Product.php

namespace App\Models;
  
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
  
class Product extends Model
{
    use HasFactory;
  
    protected $fillable = [
        'name', 'detail'
    ];
}

 

Step 5: Register Middleware

Spatie package provides its in-built middleware that way we can use it display as bellow. So, we have to add middleware in the Kernel.php file this way :

app/Http/Kernel.php

....
protected $routeMiddleware = [
    ....
    'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
    'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
    'role_or_permission' => \Spatie\Permission\Middlewares\RoleOrPermissionMiddleware::class,
]
....

 

Step 6: Create Auth

In this step, we need to set up laravel default authentication.

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

 

Step 7: Add Routes

We require to add a number of routes for the user's module, products module, and roles module. In this route i also use middleware with permission for roles and products route, so add route this way:

routes/web.php

use Illuminate\Support\Facades\Route;
  
use App\Http\Controllers\HomeController;
use App\Http\Controllers\RoleController;
use App\Http\Controllers\UserController;
use App\Http\Controllers\ProductController;
  
Route::get('/', function () {
    return view('welcome');
});
  
Auth::routes();
  
Route::get('/home', [HomeController::class, 'index'])->name('home');
  
Route::group(['middleware' => ['auth']], function() {
    Route::resource('roles', RoleController::class);
    Route::resource('users', UserController::class);
    Route::resource('products', ProductController::class);
});

 

Step 8: Create Controllers

In this step we have to add three controllers for user's module, products module and roles module so you can create three controllers like as bellow:

app/Http/Controllers/UserController.php

namespace App\Http\Controllers;
    
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Models\User;
use Spatie\Permission\Models\Role;
use DB;
use Hash;
use Illuminate\Support\Arr;
    
class UserController extends Controller
{
   
    public function index(Request $request)
    {
        $data = User::orderBy('id','DESC')->paginate(5);
        return view('users.index',compact('data'));
    }
    

    public function create()
    {
        $roles = Role::pluck('name','name')->all();
        return view('users.create',compact('roles'));
    }
    

    public function store(Request $request)
    {
        $this->validate($request, [
            'name' => 'required',
            'email' => 'required|email|unique:users,email',
            'password' => 'required|same:confirm-password',
            'roles' => 'required'
        ]);
    
        $input = $request->all();
        $input['password'] = Hash::make($input['password']);
    
        $user = User::create($input);
        $user->assignRole($request->input('roles'));
    
        return redirect()->route('users.index')
                        ->with('success','User created successfully');
    }
    
    public function show($id)
    {
        $user = User::find($id);
        return view('users.show',compact('user'));
    }
    
    public function edit($id)
    {
        $user = User::find($id);
        $roles = Role::pluck('name','name')->all();
        $userRole = $user->roles->pluck('name','name')->all();
    
        return view('users.edit',compact('user','roles','userRole'));
    }

    public function update(Request $request, $id)
    {
        $this->validate($request, [
            'name' => 'required',
            'email' => 'required|email|unique:users,email,'.$id,
            'password' => 'same:confirm-password',
            'roles' => 'required'
        ]);
    
        $input = $request->all();
        if(!empty($input['password'])){ 
            $input['password'] = Hash::make($input['password']);
        }else{
            $input = Arr::except($input,array('password'));    
        }
    
        $user = User::find($id);
        $user->update($input);
        DB::table('model_has_roles')->where('model_id',$id)->delete();
    
        $user->assignRole($request->input('roles'));
    
        return redirect()->route('users.index')
                        ->with('success','User updated successfully');
    }
    
    public function destroy($id)
    {
        User::find($id)->delete();
        return redirect()->route('users.index')
                        ->with('success','User deleted successfully');
    }
}

 

Update the productController like below

app/Http/Controllers/ProductController.php

namespace App\Http\Controllers;
    
use App\Models\Product;
use Illuminate\Http\Request;
    
class ProductController extends Controller
{ 
 
    function __construct()
    {
         $this->middleware('permission:product-list|product-create|product-edit|product-delete', ['only' => ['index','show']]);
         $this->middleware('permission:product-create', ['only' => ['create','store']]);
         $this->middleware('permission:product-edit', ['only' => ['edit','update']]);
         $this->middleware('permission:product-delete', ['only' => ['destroy']]);
    }

    public function index()
    {
        $products = Product::latest()->paginate(5);
        return view('products.index',compact('products'));
    }
    

    public function create()
    {
        return view('products.create');
    }

    public function store(Request $request)
    {
        request()->validate([
            'name' => 'required',
            'detail' => 'required',
        ]);
    
        Product::create($request->all());
    
        return redirect()->route('products.index')
                        ->with('success','Product created successfully.');
    }
    
    public function show(Product $product)
    {
        return view('products.show',compact('product'));
    }
    
    public function edit(Product $product)
    {
        return view('products.edit',compact('product'));
    }

    public function update(Request $request, Product $product)
    {
         request()->validate([
            'name' => 'required',
            'detail' => 'required',
        ]);
    
        $product->update($request->all());
    
        return redirect()->route('products.index')
                        ->with('success','Product updated successfully');
    }
    
    public function destroy(Product $product)
    {
        $product->delete();
    
        return redirect()->route('products.index')
                        ->with('success','Product deleted successfully');
    }
}

 

And update role controller as well like below

app/Http/Controllers/RoleController.php

namespace App\Http\Controllers;


use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
use DB;
    
class RoleController extends Controller
{

    function __construct()
    {
         $this->middleware('permission:role-list|role-create|role-edit|role-delete', ['only' => ['index','store']]);
         $this->middleware('permission:role-create', ['only' => ['create','store']]);
         $this->middleware('permission:role-edit', ['only' => ['edit','update']]);
         $this->middleware('permission:role-delete', ['only' => ['destroy']]);
    }
    
    public function index(Request $request)
    {
        $roles = Role::orderBy('id','DESC')->paginate(5);
        return view('roles.index',compact('roles'));
    }
    
    public function create()
    {
        $permission = Permission::get();
        return view('roles.create',compact('permission'));
    }
    
    public function store(Request $request)
    {
        $this->validate($request, [
            'name' => 'required|unique:roles,name',
            'permission' => 'required',
        ]);
    
        $role = Role::create(['name' => $request->input('name')]);
        $role->syncPermissions($request->input('permission'));
    
        return redirect()->route('roles.index')
                        ->with('success','Role created successfully');
    }

    public function show($id)
    {
        $role = Role::find($id);
        $rolePermissions = Permission::join("role_has_permissions","role_has_permissions.permission_id","=","permissions.id")
            ->where("role_has_permissions.role_id",$id)
            ->get();
    
        return view('roles.show',compact('role','rolePermissions'));
    }
    
    public function edit($id)
    {
        $role = Role::find($id);
        $permission = Permission::get();
        $rolePermissions = DB::table("role_has_permissions")->where("role_has_permissions.role_id",$id)
            ->pluck('role_has_permissions.permission_id','role_has_permissions.permission_id')
            ->all();
    
        return view('roles.edit',compact('role','permission','rolePermissions'));
    }

    public function update(Request $request, $id)
    {
        $this->validate($request, [
            'name' => 'required',
            'permission' => 'required',
        ]);
    
        $role = Role::find($id);
        $role->name = $request->input('name');
        $role->save();
    
        $role->syncPermissions($request->input('permission'));
    
        return redirect()->route('roles.index')
                        ->with('success','Role updated successfully');
    }

    public function destroy($id)
    {
        DB::table("roles")->where('id',$id)->delete();
        return redirect()->route('roles.index')
                        ->with('success','Role deleted successfully');
    }
}

 

Step 9: Create Blade Files

In this step, we need to create some frontend module to check user roles and permissions in laravel with spatie permission packages. So let's create one after another:

resources/views/layouts/app.blade.php

 

resources/views/users/index.blade.php

 

I have used collective form, but you can ignore it with normal HTML form.

resources/views/users/create.blade.php

 

resources/views/users/edit.blade.php

 

resources/views/users/show.blade.php

 

resources/views/roles/index.blade.php

 

resources/views/roles/create.blade.php

 

resources/views/roles/edit.blade.php

 

resources/views/roles/show.blade.php

 

resources/views/products/index.blade.php

 

resources/views/products/create.blade.php

 

resources/views/products/edit.blade.php

 

resources/views/products/show.blade.php

 

Step 10: Create Seeder

We need some dummy data to check user roles and permissions in laravel. We will create some fake data using laravel database seeder. let's create it:

php artisan make:seeder PermissionTableSeeder

//and then

php artisan make:seeder CreateAdminUserSeeder

 

And update the PermissionTableSeeder file this way:

database/seeds/PermissionTableSeeder.php

namespace Database\Seeders;
  
use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Permission;
  
class PermissionTableSeeder extends Seeder
{
    public function run()
    {
        $permissions = [
           'role-list',
           'role-create',
           'role-edit',
           'role-delete',
           'product-list',
           'product-create',
           'product-edit',
           'product-delete'
        ];
     
        foreach ($permissions as $permission) {
             Permission::create(['name' => $permission]);
        }
    }
}

 

And now update the admin seed like below

database/seeds/CreateAdminUserSeeder.php

namespace Database\Seeders;
  
use Illuminate\Database\Seeder;
use App\Models\User;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
  
class CreateAdminUserSeeder extends Seeder
{
   
    public function run()
    {
        $user = User::create([
            'name' => 'Hardik Savani', 
            'email' => 'admin@gmail.com',
            'password' => bcrypt('123456')
        ]);
    
        $role = Role::create(['name' => 'Admin']);
     
        $permissions = Permission::pluck('id','id')->all();
   
        $role->syncPermissions($permissions);
     
        $user->assignRole([$role->id]);
    }
}

 

Now run db:seed command to generate some fake data.

 

php artisan db:seed --class=PermissionTableSeeder

php artisan db:seed --class=CreateAdminUserSeeder

 

Now we are ready to run full example of ACL. so let's run our example so run bellow command to start the server:

php artisan serve

 

And visit the below url to check:

 

URL
http://localhost:8000/

 

Recommended: Vue Laravel CRUD Example With Vue Router & Sweet Alert

 

Now all are set to go. Hope it can help you.

 

Facebook Github
A web enthusiastic, a self-motivated full-stack software engineer from Dhaka, Bangladesh with experience in developing applications using Laravel , React and Vue js