In this tutorial, we will learn how to Install Spatie Package and create Permission CRUD, Roles CRUD, Assign/Add Permissions to a Role, and User CRUD with attaching roles to it.
Also, how to use these Roles and Permissions to manage and protect the routes in the Laravel application. So basically, protecting routes means when a user is trying to access a certain route, to which he does not have role or permission, then we will not allow the user to access that route/page.
So let's get started:
Install Laravel with the following command:
composer create-project laravel/laravel Roles-and-Permissions-Laravel-10
Setup the Database in .env
file
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laraveltutorial
DB_USERNAME=root
DB_PASSWORD=
Install the Authentication package as per your requirement. So I will use Laravel Breeze
composer require laravel/breeze --dev
After successfully installing the Laravel Breeze package, run the below artisan command to install in the application.
php artisan breeze:install blade
once breeze:install is completed, run the below 3 commands:
php artisan migrate
npm install
npm run dev
2.1: install the package via composer
composer require spatie/laravel-permission
After installing the spatie larvel permission package.
2.2: The service provider will automatically get registered. Or you may manually add the service provider in your config/app.php
file:
'providers' => [
// ...
Spatie\Permission\PermissionServiceProvider::class,
];
2.3: You should publish the migration and the config/permission.php
config file with
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
2.4: Run the migrations
php artisan migrate
This package comes with RoleMiddleware
, PermissionMiddleware
and RoleOrPermissionMiddleware
middleware. You can add them inside your app/Http/Kernel.php
file to be able to use them through aliases.
// Note: Laravel 10+ uses $middlewareAliases = [
protected $middlewareAliases = [
// ...
'role' => \Spatie\Permission\Middleware\RoleMiddleware::class,
'permission' => \Spatie\Permission\Middleware\PermissionMiddleware::class,
'role_or_permission' => \Spatie\Permission\Middleware\RoleOrPermissionMiddleware::class,
];
class User extends Authenticatable
{
use HasRoles;
...
Route::group(['middleware' => ['role:super-admin|admin']], function() {
Route::resource('permissions', App\Http\Controllers\PermissionController::class);
Route::get('permissions/{permissionId}/delete', [App\Http\Controllers\PermissionController::class, 'destroy']);
Route::resource('roles', App\Http\Controllers\RoleController::class);
Route::get('roles/{roleId}/delete', [App\Http\Controllers\RoleController::class, 'destroy']);
Route::get('roles/{roleId}/give-permissions', [App\Http\Controllers\RoleController::class, 'addPermissionToRole']);
Route::put('roles/{roleId}/give-permissions', [App\Http\Controllers\RoleController::class, 'givePermissionToRole']);
Route::resource('users', App\Http\Controllers\UserController::class);
Route::get('users/{userId}/delete', [App\Http\Controllers\UserController::class, 'destroy']);
});
6.1: Create RoleController with the following command:
php artisan make:controller RoleController
after successfully creating the controller, paste the below code:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
use Illuminate\Support\Facades\DB;
class RoleController extends Controller
{
public function __construct()
{
$this->middleware('permission:view role', ['only' => ['index']]);
$this->middleware('permission:create role', ['only' => ['create','store','addPermissionToRole','givePermissionToRole']]);
$this->middleware('permission:update role', ['only' => ['update','edit']]);
$this->middleware('permission:delete role', ['only' => ['destroy']]);
}
public function index()
{
$roles = Role::get();
return view('role-permission.role.index', ['roles' => $roles]);
}
public function create()
{
return view('role-permission.role.create');
}
public function store(Request $request)
{
$request->validate([
'name' => [
'required',
'string',
'unique:roles,name'
]
]);
Role::create([
'name' => $request->name
]);
return redirect('roles')->with('status','Role Created Successfully');
}
public function edit(Role $role)
{
return view('role-permission.role.edit',[
'role' => $role
]);
}
public function update(Request $request, Role $role)
{
$request->validate([
'name' => [
'required',
'string',
'unique:roles,name,'.$role->id
]
]);
$role->update([
'name' => $request->name
]);
return redirect('roles')->with('status','Role Updated Successfully');
}
public function destroy($roleId)
{
$role = Role::find($roleId);
$role->delete();
return redirect('roles')->with('status','Role Deleted Successfully');
}
public function addPermissionToRole($roleId)
{
$permissions = Permission::get();
$role = Role::findOrFail($roleId);
$rolePermissions = DB::table('role_has_permissions')
->where('role_has_permissions.role_id', $role->id)
->pluck('role_has_permissions.permission_id','role_has_permissions.permission_id')
->all();
return view('role-permission.role.add-permissions', [
'role' => $role,
'permissions' => $permissions,
'rolePermissions' => $rolePermissions
]);
}
public function givePermissionToRole(Request $request, $roleId)
{
$request->validate([
'permission' => 'required'
]);
$role = Role::findOrFail($roleId);
$role->syncPermissions($request->permission);
return redirect()->back()->with('status','Permissions added to role');
}
}
6.2: Create PermissionController with the following command:
php artisan make:controller PermissionController
after successfully creating the controller, paste the below code:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Spatie\Permission\Models\Permission;
class PermissionController extends Controller
{
public function __construct()
{
$this->middleware('permission:view permission', ['only' => ['index']]);
$this->middleware('permission:create permission', ['only' => ['create','store']]);
$this->middleware('permission:update permission', ['only' => ['update','edit']]);
$this->middleware('permission:delete permission', ['only' => ['destroy']]);
}
public function index()
{
$permissions = Permission::get();
return view('role-permission.permission.index', ['permissions' => $permissions]);
}
public function create()
{
return view('role-permission.permission.create');
}
public function store(Request $request)
{
$request->validate([
'name' => [
'required',
'string',
'unique:permissions,name'
]
]);
Permission::create([
'name' => $request->name
]);
return redirect('permissions')->with('status','Permission Created Successfully');
}
public function edit(Permission $permission)
{
return view('role-permission.permission.edit', ['permission' => $permission]);
}
public function update(Request $request, Permission $permission)
{
$request->validate([
'name' => [
'required',
'string',
'unique:permissions,name,'.$permission->id
]
]);
$permission->update([
'name' => $request->name
]);
return redirect('permissions')->with('status','Permission Updated Successfully');
}
public function destroy($permissionId)
{
$permission = Permission::find($permissionId);
$permission->delete();
return redirect('permissions')->with('status','Permission Deleted Successfully');
}
}
6.3: Create UserController with the following command:
php artisan make:controller UserController
after successfully creating the controller, paste the below code:
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\Request;
use Spatie\Permission\Models\Role;
use Illuminate\Support\Facades\Hash;
class UserController extends Controller
{
public function __construct()
{
$this->middleware('permission:view user', ['only' => ['index']]);
$this->middleware('permission:create user', ['only' => ['create','store']]);
$this->middleware('permission:update user', ['only' => ['update','edit']]);
$this->middleware('permission:delete user', ['only' => ['destroy']]);
}
public function index()
{
$users = User::get();
return view('role-permission.user.index', ['users' => $users]);
}
public function create()
{
$roles = Role::pluck('name','name')->all();
return view('role-permission.user.create', ['roles' => $roles]);
}
public function store(Request $request)
{
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|max:255|unique:users,email',
'password' => 'required|string|min:8|max:20',
'roles' => 'required'
]);
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
$user->syncRoles($request->roles);
return redirect('/users')->with('status','User created successfully with roles');
}
public function edit(User $user)
{
$roles = Role::pluck('name','name')->all();
$userRoles = $user->roles->pluck('name','name')->all();
return view('role-permission.user.edit', [
'user' => $user,
'roles' => $roles,
'userRoles' => $userRoles
]);
}
public function update(Request $request, User $user)
{
$request->validate([
'name' => 'required|string|max:255',
'password' => 'nullable|string|min:8|max:20',
'roles' => 'required'
]);
$data = [
'name' => $request->name,
'email' => $request->email,
];
if(!empty($request->password)){
$data += [
'password' => Hash::make($request->password),
];
}
$user->update($data);
$user->syncRoles($request->roles);
return redirect('/users')->with('status','User Updated Successfully with roles');
}
public function destroy($userId)
{
$user = User::findOrFail($userId);
$user->delete();
return redirect('/users')->with('status','User Delete Successfully');
}
}
<x-app-layout>
<div class="container mt-5">
<div class="row">
<div class="col-md-12">
@if ($errors->any())
<ul class="alert alert-warning">
@foreach ($errors->all() as $error)
<li>{{$error}}</li>
@endforeach
</ul>
@endif
<div class="card">
<div class="card-header">
<h4>Create Role
<a href="{{ url('roles') }}" class="btn btn-danger float-end">Back</a>
</h4>
</div>
<div class="card-body">
<form action="{{ url('roles') }}" method="POST">
@csrf
<div class="mb-3">
<label for="">Role Name</label>
<input type="text" name="name" class="form-control" />
</div>
<div class="mb-3">
<button type="submit" class="btn btn-primary">Save</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</x-app-layout>
<x-app-layout>
<div class="container mt-5">
<div class="row">
<div class="col-md-12">
@if ($errors->any())
<ul class="alert alert-warning">
@foreach ($errors->all() as $error)
<li>{{$error}}</li>
@endforeach
</ul>
@endif
<div class="card">
<div class="card-header">
<h4>Edit Role
<a href="{{ url('roles') }}" class="btn btn-danger float-end">Back</a>
</h4>
</div>
<div class="card-body">
<form action="{{ url('roles/'.$role->id) }}" method="POST">
@csrf
@method('PUT')
<div class="mb-3">
<label for="">Role Name</label>
<input type="text" name="name" value="{{ $role->name }}" class="form-control" />
</div>
<div class="mb-3">
<button type="submit" class="btn btn-primary">Update</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</x-app-layout>
<x-app-layout>
<div class="container mt-5">
<a href="{{ url('roles') }}" class="btn btn-primary mx-1">Roles</a>
<a href="{{ url('permissions') }}" class="btn btn-info mx-1">Permissions</a>
<a href="{{ url('users') }}" class="btn btn-warning mx-1">Users</a>
</div>
<div class="container mt-2">
<div class="row">
<div class="col-md-12">
@if (session('status'))
<div class="alert alert-success">{{ session('status') }}</div>
@endif
<div class="card mt-3">
<div class="card-header">
<h4>
Roles
@can('create role')
<a href="{{ url('roles/create') }}" class="btn btn-primary float-end">Add Role</a>
@endcan
</h4>
</div>
<div class="card-body">
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th width="40%">Action</th>
</tr>
</thead>
<tbody>
@foreach ($roles as $role)
<tr>
<td>{{ $role->id }}</td>
<td>{{ $role->name }}</td>
<td>
<a href="{{ url('roles/'.$role->id.'/give-permissions') }}" class="btn btn-warning">
Add / Edit Role Permission
</a>
@can('update role')
<a href="{{ url('roles/'.$role->id.'/edit') }}" class="btn btn-success">
Edit
</a>
@endcan
@can('delete role')
<a href="{{ url('roles/'.$role->id.'/delete') }}" class="btn btn-danger mx-2">
Delete
</a>
@endcan
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</x-app-layout>
<x-app-layout>
<div class="container mt-5">
<div class="row">
<div class="col-md-12">
@if (session('status'))
<div class="alert alert-success">{{ session('status') }}</div>
@endif
<div class="card">
<div class="card-header">
<h4>Role : {{ $role->name }}
<a href="{{ url('roles') }}" class="btn btn-danger float-end">Back</a>
</h4>
</div>
<div class="card-body">
<form action="{{ url('roles/'.$role->id.'/give-permissions') }}" method="POST">
@csrf
@method('PUT')
<div class="mb-3">
@error('permission')
<span class="text-danger">{{ $message }}</span>
@enderror
<label for="">Permissions</label>
<div class="row">
@foreach ($permissions as $permission)
<div class="col-md-2">
<label>
<input
type="checkbox"
name="permission[]"
value="{{ $permission->name }}"
{{ in_array($permission->id, $rolePermissions) ? 'checked':'' }}
/>
{{ $permission->name }}
</label>
</div>
@endforeach
</div>
</div>
<div class="mb-3">
<button type="submit" class="btn btn-primary">Update</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</x-app-layout>
<x-app-layout>
<div class="container mt-5">
<div class="row">
<div class="col-md-12">
@if ($errors->any())
<ul class="alert alert-warning">
@foreach ($errors->all() as $error)
<li>{{$error}}</li>
@endforeach
</ul>
@endif
<div class="card">
<div class="card-header">
<h4>Create Permission
<a href="{{ url('permissions') }}" class="btn btn-danger float-end">Back</a>
</h4>
</div>
<div class="card-body">
<form action="{{ url('permissions') }}" method="POST">
@csrf
<div class="mb-3">
<label for="">Permission Name</label>
<input type="text" name="name" class="form-control" />
</div>
<div class="mb-3">
<button type="submit" class="btn btn-primary">Save</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</x-app-layout>
<x-app-layout>
<div class="container mt-5">
<div class="row">
<div class="col-md-12">
@if ($errors->any())
<ul class="alert alert-warning">
@foreach ($errors->all() as $error)
<li>{{$error}}</li>
@endforeach
</ul>
@endif
<div class="card">
<div class="card-header">
<h4>Edit Permission
<a href="{{ url('permissions') }}" class="btn btn-danger float-end">Back</a>
</h4>
</div>
<div class="card-body">
<form action="{{ url('permissions/'.$permission->id) }}" method="POST">
@csrf
@method('PUT')
<div class="mb-3">
<label for="">Permission Name</label>
<input type="text" name="name" value="{{ $permission->name }}" class="form-control" />
</div>
<div class="mb-3">
<button type="submit" class="btn btn-primary">Update</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</x-app-layout>
<x-app-layout>
<div class="container mt-5">
<a href="{{ url('roles') }}" class="btn btn-primary mx-1">Roles</a>
<a href="{{ url('permissions') }}" class="btn btn-info mx-1">Permissions</a>
<a href="{{ url('users') }}" class="btn btn-warning mx-1">Users</a>
</div>
<div class="container mt-2">
<div class="row">
<div class="col-md-12">
@if (session('status'))
<div class="alert alert-success">{{ session('status') }}</div>
@endif
<div class="card mt-3">
<div class="card-header">
<h4>Permissions
@can('create permission')
<a href="{{ url('permissions/create') }}" class="btn btn-primary float-end">Add Permission</a>
@endcan
</h4>
</div>
<div class="card-body">
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th width="40%">Action</th>
</tr>
</thead>
<tbody>
@foreach ($permissions as $permission)
<tr>
<td>{{ $permission->id }}</td>
<td>{{ $permission->name }}</td>
<td>
@can('update permission')
<a href="{{ url('permissions/'.$permission->id.'/edit') }}" class="btn btn-success">Edit</a>
@endcan
@can('delete permission')
<a href="{{ url('permissions/'.$permission->id.'/delete') }}" class="btn btn-danger mx-2">Delete</a>
@endcan
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</x-app-layout>
<x-app-layout>
<div class="container mt-5">
<div class="row">
<div class="col-md-12">
@if ($errors->any())
<ul class="alert alert-warning">
@foreach ($errors->all() as $error)
<li>{{$error}}</li>
@endforeach
</ul>
@endif
<div class="card">
<div class="card-header">
<h4>Create User
<a href="{{ url('users') }}" class="btn btn-danger float-end">Back</a>
</h4>
</div>
<div class="card-body">
<form action="{{ url('users') }}" method="POST">
@csrf
<div class="mb-3">
<label for="">Name</label>
<input type="text" name="name" class="form-control" />
</div>
<div class="mb-3">
<label for="">Email</label>
<input type="text" name="email" class="form-control" />
</div>
<div class="mb-3">
<label for="">Password</label>
<input type="text" name="password" class="form-control" />
</div>
<div class="mb-3">
<label for="">Roles</label>
<select name="roles[]" class="form-control" multiple>
<option value="">Select Role</option>
@foreach ($roles as $role)
<option value="{{ $role }}">{{ $role }}</option>
@endforeach
</select>
</div>
<div class="mb-3">
<button type="submit" class="btn btn-primary">Save</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</x-app-layout>
<x-app-layout>
<div class="container mt-5">
<div class="row">
<div class="col-md-12">
@if ($errors->any())
<ul class="alert alert-warning">
@foreach ($errors->all() as $error)
<li>{{$error}}</li>
@endforeach
</ul>
@endif
<div class="card">
<div class="card-header">
<h4>Edit User
<a href="{{ url('users') }}" class="btn btn-danger float-end">Back</a>
</h4>
</div>
<div class="card-body">
<form action="{{ url('users/'.$user->id) }}" method="POST">
@csrf
@method('PUT')
<div class="mb-3">
<label for="">Name</label>
<input type="text" name="name" value="{{ $user->name }}" class="form-control" />
@error('name') <span class="text-danger">{{ $message }}</span> @enderror
</div>
<div class="mb-3">
<label for="">Email</label>
<input type="text" name="email" readonly value="{{ $user->email }}" class="form-control" />
</div>
<div class="mb-3">
<label for="">Password</label>
<input type="text" name="password" class="form-control" />
@error('password') <span class="text-danger">{{ $message }}</span> @enderror
</div>
<div class="mb-3">
<label for="">Roles</label>
<select name="roles[]" class="form-control" multiple>
<option value="">Select Role</option>
@foreach ($roles as $role)
<option
value="{{ $role }}"
{{ in_array($role, $userRoles) ? 'selected':'' }}
>
{{ $role }}
</option>
@endforeach
</select>
@error('roles') <span class="text-danger">{{ $message }}</span> @enderror
</div>
<div class="mb-3">
<button type="submit" class="btn btn-primary">Update</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</x-app-layout>
<x-app-layout>
<div class="container mt-5">
<a href="{{ url('roles') }}" class="btn btn-primary mx-1">Roles</a>
<a href="{{ url('permissions') }}" class="btn btn-info mx-1">Permissions</a>
<a href="{{ url('users') }}" class="btn btn-warning mx-1">Users</a>
</div>
<div class="container mt-2">
<div class="row">
<div class="col-md-12">
@if (session('status'))
<div class="alert alert-success">{{ session('status') }}</div>
@endif
<div class="card mt-3">
<div class="card-header">
<h4>Users
@can('create user')
<a href="{{ url('users/create') }}" class="btn btn-primary float-end">Add User</a>
@endcan
</h4>
</div>
<div class="card-body">
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Email</th>
<th>Roles</th>
<th>Action</th>
</tr>
</thead>
<tbody>
@foreach ($users as $user)
<tr>
<td>{{ $user->id }}</td>
<td>{{ $user->name }}</td>
<td>{{ $user->email }}</td>
<td>
@if (!empty($user->getRoleNames()))
@foreach ($user->getRoleNames() as $rolename)
<label class="badge bg-primary mx-1">{{ $rolename }}</label>
@endforeach
@endif
</td>
<td>
@can('update user')
<a href="{{ url('users/'.$user->id.'/edit') }}" class="btn btn-success">Edit</a>
@endcan
@can('delete user')
<a href="{{ url('users/'.$user->id.'/delete') }}" class="btn btn-danger mx-2">Delete</a>
@endcan
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</x-app-layout>
Step 10: Create required Roles and Permission with the help of Laravel Seeder to start with the Project
create the seeder with the following command:
php artisan make:seeder UserRolePermissionSeeder
After successfully creating the seeder, go to its path database/seeders/UserRolePermissionSeeder.php
file and paste the below code
<?php
namespace Database\Seeders;
use App\Models\User;
use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Role;
use Illuminate\Support\Facades\Hash;
use Spatie\Permission\Models\Permission;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
class UserRolePermissionSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
// Create Permissions
Permission::create(['name' => 'view role']);
Permission::create(['name' => 'create role']);
Permission::create(['name' => 'update role']);
Permission::create(['name' => 'delete role']);
Permission::create(['name' => 'view permission']);
Permission::create(['name' => 'create permission']);
Permission::create(['name' => 'update permission']);
Permission::create(['name' => 'delete permission']);
Permission::create(['name' => 'view user']);
Permission::create(['name' => 'create user']);
Permission::create(['name' => 'update user']);
Permission::create(['name' => 'delete user']);
Permission::create(['name' => 'view product']);
Permission::create(['name' => 'create product']);
Permission::create(['name' => 'update product']);
Permission::create(['name' => 'delete product']);
// Create Roles
$superAdminRole = Role::create(['name' => 'super-admin']); //as super-admin
$adminRole = Role::create(['name' => 'admin']);
$staffRole = Role::create(['name' => 'staff']);
$userRole = Role::create(['name' => 'user']);
// Lets give all permission to super-admin role.
$allPermissionNames = Permission::pluck('name')->toArray();
$superAdminRole->givePermissionTo($allPermissionNames);
// Let's give few permissions to admin role.
$adminRole->givePermissionTo(['create role', 'view role', 'update role']);
$adminRole->givePermissionTo(['create permission', 'view permission']);
$adminRole->givePermissionTo(['create user', 'view user', 'update user']);
$adminRole->givePermissionTo(['create product', 'view product', 'update product']);
// Let's Create User and assign Role to it.
$superAdminUser = User::firstOrCreate([
'email' => 'superadmin@gmail.com',
], [
'name' => 'Super Admin',
'email' => 'superadmin@gmail.com',
'password' => Hash::make ('12345678'),
]);
$superAdminUser->assignRole($superAdminRole);
$adminUser = User::firstOrCreate([
'email' => 'admin@gmail.com'
], [
'name' => 'Admin',
'email' => 'admin@gmail.com',
'password' => Hash::make ('12345678'),
]);
$adminUser->assignRole($adminRole);
$staffUser = User::firstOrCreate([
'email' => 'staff@gmail.com',
], [
'name' => 'Staff',
'email' => 'staff@gmail.com',
'password' => Hash::make('12345678'),
]);
$staffUser->assignRole($staffRole);
}
}
Now, let's run the Seeder to migration into database with the following command:
php artisan db:seed --class="UserRolePermissionSeeder"
Let's serve the application and use the Roles and Permission in Laravel.
php artisan serve