DEV Community

Hilmi Hidayat
Hilmi Hidayat

Posted on

How to Implement Laravel Permission Package for Advanced User Roles

Laravel Permission - Managing user roles and permissions is a crucial aspect of building secure and scalable web applications. Whether you're developing an e-commerce platform, a content management system, or a SaaS solution, defining what users can and cannot do ensures both functionality and security. Laravel, known for its elegant syntax and robust ecosystem, provides excellent tools to streamline this process.

One such tool is the Laravel Permission package, a flexible and powerful solution that simplifies handling user roles and permissions. This package allows developers to implement granular access control, offering the ability to assign roles, define permissions, and enforce them across various parts of the application with ease.

In this blog, we’ll guide you step-by-step on how to set up and use the Laravel Permission package to create advanced user role hierarchies. From installation to best practices, you'll learn how to harness its capabilities to build a secure, efficient, and well-structured application. Let’s dive in and elevate your Laravel projects with sophisticated permission management!

Getting Started

In this article, I will try to explain everything in as much detail as possible, from installing Laravel and the required packages to implementing permissions using blade directives and middleware.

Install Fresh Laravel Project

Ok, let's start by installing a fresh Laravel project.

laravel new laravel-permission
Enter fullscreen mode Exit fullscreen mode

Please install Laravel using the Laravel Installer as per the command above, or you can also use other methods as explained in the Laravel documentation.

cd laravel-permission
Enter fullscreen mode Exit fullscreen mode

If the installation process is successful, let's enter the project directory using the command as in the example above.

Install Laravel UI

In this tutorial, we will need an authentication system. I will use the Laravel UI package to create a simple authentication system using Bootstrap.

composer require laravel/ui
php artisan ui bootstrap --auth
npm install && npm run dev
Enter fullscreen mode Exit fullscreen mode

It's very easy to install the Laravel UI package. Please run the commands above sequentially on your terminal.

User Management

Okay, we already have an authentication system in our Laravel project. Next, let's create an admin user using Seeder.

php artisan make:seeder UserSeeder
Enter fullscreen mode Exit fullscreen mode

Run the artisan command as above to create the UserSeeder class.

<?php

namespace Database\Seeders;

use App\Models\User;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;

class UserSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        $user = User::create([
            'name'     => 'Admin',
            'email'    => 'admin@gmail.com',
            'password' => bcrypt('password')
        ]);
    }
}
Enter fullscreen mode Exit fullscreen mode

If the UserSeeder class has been successfully created, open the file and add the script as above. So, with this class, we will create an Admin user.

php artisan db:seed --class=UserSeeder
Enter fullscreen mode Exit fullscreen mode

Run the command as above to execute the UserSeeder class. If so, then we have an Admin user account for this project.

Okay, next, let's create user management. This will allow us to create and update user details such as name, email, and role.

php artisan make:controller UserController
Enter fullscreen mode Exit fullscreen mode

Please run the artisan command as above to create a UserController file.

routes/web.php

Route::resource('users', App\Http\Controllers\UserController::class);
Enter fullscreen mode Exit fullscreen mode

Open the routes/web.php file, then add route resources that we direct to the UserController.

Let's create a UserRequest class to handle validation on UserController.

php artisan make:request UserRequest
Enter fullscreen mode Exit fullscreen mode

Please run the artisan command as above to create the Request file.

UserRequest.php

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class UserRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     */
    public function authorize(): bool
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
     */
    public function rules(): array
    {
        return [
            'name'     => 'required',
            'email'    => 'required|email|unique:users,email,' . $this->user->id,
        ];
    }
}
Enter fullscreen mode Exit fullscreen mode

Open the UserRequest file we successfully generated earlier. Then, add variables like the example above to the function rules. Here, we will only validate the name and email input.

Okay, next, let's work on the UserController.

UserController.php

<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;
use App\Http\Requests\UserRequest;

class UserController extends Controller
{
    public function index()
    {
        return view('users.index',[
            'users' => User::paginate(10)->withQueryString()
        ]);
    }

    public function create()
    {
        return view('users.form',[
            'user'   => new User(),
            'submit' => 'Create',
            'action' => route('users.store'),
            'method' => 'POST',
            'title'  => 'Create User'
        ]);
    }

    public function store(UserRequest $request)
    {
        $user = User::create([
            'name'     => $request->name,
            'email'    => $request->email,
            'password' => bcrypt('password')
        ]);

        return to_route('users.index')->with('success', 'User created successfully');
    }

    public function edit(User $user)
    {
        return view('users.form',[
            'user'   => $user,
            'submit' => 'Update',
            'action' => route('users.update', $user),
            'method' => 'PUT',
            'title'  => 'Edit User'
        ]);
    }

    public function update(UserRequest $request, User $user)
    {
        $user->update([
            'name'     => $request->name,
            'email'    => $request->email,
        ]);

        return to_route('users.index')->with('success', 'User updated successfully');
    }
}
Enter fullscreen mode Exit fullscreen mode

I will convey this step's very basic information. Here, we will create an index method to return data from the User model and direct it to the users/index.blade.php view. I will also explain the use of reusable forms for our case study this time. We will only use one and the same view file for the create and edit forms.

Create Dynamic Title

Before we create a view for user management, let's make a dynamic title.

layouts/app.blade.php

Open the app.blade.php file, look for the code as below.

<title>{{ config('app.name', 'Laravel') }}</title>
Enter fullscreen mode Exit fullscreen mode

Please change it to be like the code below.

<title>
    @hasSection('title')
        @yield('title') - {{ config('app.name', 'Laravel') }}
    @else
        {{ config('app.name', 'Laravel') }}
    @endif
</title>
Enter fullscreen mode Exit fullscreen mode

With the code above, if we add @section('title') to the rendered view, the title that will be displayed is the section's value.

<!-- Left Side Of Navbar -->
<ul class="navbar-nav me-auto">

</ul>
Enter fullscreen mode Exit fullscreen mode

And next, let's add the Users menu. Find the code as above, then add a new menu like the example code below.

<!-- Left Side Of Navbar -->
<ul class="navbar-nav me-auto">
    <li class="nav-item">
        <a class="nav-link" href="{{ route('users.index') }}">{{ __('Users') }}</a>
    </li>
</ul>
Enter fullscreen mode Exit fullscreen mode

That way, we will have a new menu on the navbar.

Okay, next, let's create a new folder inside the resources/views folder named users and create an index.blade.php file inside that folder.

users/index.blade.php

@extends('layouts.app')
@section('title', 'Users')
@section('content')
<div class="container">
    <div class="row">
        <div class="col-12">
            <div class="card">
                <div class="card-header d-flex justify-content-between">
                    <h1 class="fs-5">Users</h1>
                    <a href="{{ route('users.create') }}" class="btn btn-sm btn-primary">Create</a>
                </div>
                <div class="card-body">
                    @if (session('success'))
                    <div class="alert alert-success" role="alert">
                        {{ session('success') }}
                    </div>
                    @endif
                    <table class="table">
                        <thead>
                            <tr>
                                <th scope="col">#</th>
                                <th scope="col">Name</th>
                                <th scope="col">Email</th>
                                <th scope="col">Roles</th>
                                <th scope="col" class="text-end">Action</th>
                            </tr>
                        </thead>
                        <tbody>
                            @forelse ($users as $user)
                            <tr>
                                <th scope="row">{{ $user->id }}</th>
                                <td>{{ $user->name }}</td>
                                <td>{{ $user->email }}</td>
                                <td>-</td>
                                <td class="text-end">
                                    <a href="{{ route('users.edit', $user->id) }}" class="btn btn-sm btn-primary">Edit</a>
                                </td>
                                @empty
                                <td colspan="5">No users found</td>
                                @endforelse
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection
Enter fullscreen mode Exit fullscreen mode

Add the above code to the index.blade.php file we just created. This is a simple view, containing a users table and a create button to create a new user.

users/form.blade.php

@extends('layouts.app')
@section('title',"{$title}")
@section('content')
<div class="container">
    <div class="row d-flex justify-content-center">
        <div class="col-md-6">
            <div class="card">
                <div class="card-body">
                    <form action="{{ $action }}" method="post">
                        @if($method == 'PUT')
                        @method('PUT')
                        @endif
                        @csrf
                        <div class="mb-3">
                            <label for="name" class="form-label">Name</label>
                            <input type="text" class="form-control @error('name') is-invalid @enderror" id="name" name="name" value="{{ old('name') ?? $user->name }}">
                            @error('name')
                            <div class="invalid-feedback">
                                {{ $message }}
                            </div>
                            @enderror
                        </div>
                        <div class="mb-3">
                            <label for="email" class="form-label">Email</label>
                            <input type="email" class="form-control @error('email') is-invalid @enderror" id="email" name="email" value="{{ old('email') ?? $user->email }}">
                            @error('email')
                            <div class="invalid-feedback">
                                {{ $message }}
                            </div>
                            @enderror
                        </div>
                        <button type="submit" class="btn btn-primary">
                        {{ $submit }}
                        </button>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection
Enter fullscreen mode Exit fullscreen mode

Next, create a form.blade.php file inside the users folder and add the code above. In this form, we still only have the name and email input fields. We will add the role input field later.

Let's try the user management. Run the project with the php artisan server command, then login using the account we created using the seeder earlier.

The following is the user management view that we have created.

users table

users form

OK, up to this stage, we have successfully created user management.

Roles and Permission Management

We have arrived at the core explanation. At this stage, I will explain everything from installing the Laravel permissions package to creating role management and using Laravel permissions in basic usage.

Install and Setup Laravel Permissions Package

composer require spatie/laravel-permission
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
php artisan migrate
Enter fullscreen mode Exit fullscreen mode

Please install Laravel permissions package using the command as above in sequence.

models/User.php

use Spatie\Permission\Traits\HasRoles;
...
...
use HasRoles;
Enter fullscreen mode Exit fullscreen mode

Open the User model file and add the HasRoles trait as in the example above.

php artisan make:seeder RoleSeeder
php artisan make:seeder PermissionSeeder
Enter fullscreen mode Exit fullscreen mode

Create RoleSeeder and PermissionSeeder files by running the artisan commands as above.

RoleSeeder

<?php

namespace Database\Seeders;

use App\Models\User;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;

class RoleSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        $roles = [
            [
                'name' => 'admin',
                'guard_name' => 'web',
                'created_at' => now(),
                'updated_at' => now()
            ],
            [
                'name' => 'editor',
                'guard_name' => 'web',
                'created_at' => now(),
                'updated_at' => now()
            ]
        ];

        DB::table('roles')->insert($roles);

        $admin = User::where('email', 'admin@gmail.com')->first();

        if ($admin) {
            $admin->assignRole('admin');
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Open the RoleSeeder.php file and change the code in it to be like the example above. Here, we will create admin and editor roles and apply the admin role to the admin user.

php artisan db:seed --class=RoleSeeder
Enter fullscreen mode Exit fullscreen mode

Execute the RoleSeeder file by running the seeder command as above.

PermissionSeeder.php

<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Spatie\Permission\Models\Role;

class PermissionSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        $permissions = [
            [
                'name' => 'user table',
                'guard_name' => 'web',
                'created_at' => now(),
                'updated_at' => now()
            ],
            [
                'name' => 'create user',
                'guard_name' => 'web',
                'created_at' => now(),
                'updated_at' => now()
            ],
            [
                'name' => 'edit user',
                'guard_name' => 'web',
                'created_at' => now(),
                'updated_at' => now()
            ],
            [
                'name' => 'delete user',
                'guard_name' => 'web',
                'created_at' => now(),
                'updated_at' => now()
            ],
            [
                'name' => 'post table',
                'guard_name' => 'web',
                'created_at' => now(),
                'updated_at' => now()
            ],
            [
                'name' => 'create post',
                'guard_name' => 'web',
                'created_at' => now(),
                'updated_at' => now()
            ],
            [
                'name' => 'edit post',
                'guard_name' => 'web',
                'created_at' => now(),
                'updated_at' => now()
            ],
            [
                'name' => 'delete post',
                'guard_name' => 'web',
                'created_at' => now(),
                'updated_at' => now()
            ],
            [
                'name' => 'role table',
                'guard_name' => 'web',
                'created_at' => now(),
                'updated_at' => now()
            ],
            [
                'name' => 'edit role',
                'guard_name' => 'web',
                'created_at' => now(),
                'updated_at' => now()
            ]
        ];

        DB::table('permissions')->insert($permissions);

        $adminRole = Role::where('name', 'admin')->first();

        if ($adminRole) {
            $permissions = DB::table('permissions')->pluck('id')->toArray();
            $adminRole->syncPermissions($permissions);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Next, open the PermissionSeeder file and change its code to be like the example above. Here, we will create permissions and apply all of those permissions to the admin role.

php artisan db:seed --class=PermissionSeeder
Enter fullscreen mode Exit fullscreen mode

Execute the PermissionSeeder file using the artisan command as in the example above.

Let's update the users/index.blade.php view file.

Open users/index.blade.php, look for the code as below.

<td>-</td>
Enter fullscreen mode Exit fullscreen mode

Then, change it to be like below.

<td>
    @forelse($user->roles as $role)
    <span class="badge bg-dark">{{ $role->name }}</span>
    @empty
    -
    @endforelse
</td>
Enter fullscreen mode Exit fullscreen mode

With the code above, we will display the roles owned by the user.

Next, let's adjust the code we created in UserController. Change the code below.

public function create()
{
    return view('users.form',[
        'user'   => new User(),
        'submit' => 'Create',
        'action' => route('users.store'),
        'method' => 'POST',
        'title'  => 'Create User'
    ]);
}
Enter fullscreen mode Exit fullscreen mode

Please change it to something like the one below.

public function create()
{
    return view('users.form',[
        'user'   => new User(),
        'submit' => 'Create',
        'action' => route('users.store'),
        'method' => 'POST',
        'title'  => 'Create User',
        'roles'  => Role::get()
    ]);
}
Enter fullscreen mode Exit fullscreen mode

Here, we add the roles variable to get roles data from the Role model.

public function store(UserRequest $request)
{
    $user = User::create([
        'name'     => $request->name,
        'email'    => $request->email,
        'password' => bcrypt('password')
    ]);

    return to_route('users.index')->with('success', 'User created successfully');
}
Enter fullscreen mode Exit fullscreen mode

Then, move on to the other method. Change the store method to the one below.

public function store(UserRequest $request)
{
    $user = User::create([
        'name'     => $request->name,
        'email'    => $request->email,
        'password' => bcrypt('password')
    ]);

    $user->assignRole($request->role);

    return to_route('users.index')->with('success', 'User created successfully');
}
Enter fullscreen mode Exit fullscreen mode

Here we add the script $user->assignRole($request->role); to save the role that the user has selected.

public function edit(User $user)
{
    return view('users.form',[
        'user'   => $user,
        'submit' => 'Update',
        'action' => route('users.update', $user),
        'method' => 'PUT',
        'title'  => 'Edit User'
    ]);
}
Enter fullscreen mode Exit fullscreen mode

Then, in the edit method, change it to something like below.

public function edit(User $user)
{
    return view('users.form',[
        'user'   => $user,
        'submit' => 'Update',
        'action' => route('users.update', $user),
        'method' => 'PUT',
        'title'  => 'Edit User',
        'roles'  => Role::get()
    ]);
}
Enter fullscreen mode Exit fullscreen mode

The edit method is the same. Here, we add the roles variable to get role data from the Role model.

public function update(UserRequest $request, User $user)
{
    $user->update([
        'name'     => $request->name,
        'email'    => $request->email,
    ]);

    return to_route('users.index')->with('success', 'User updated successfully');
}
Enter fullscreen mode Exit fullscreen mode

Next, adjust the update method to be as below.

public function update(UserRequest $request, User $user)
{
    $user->update([
        'name'     => $request->name,
        'email'    => $request->email,
    ]);

    $user->syncRoles($request->role);

    return to_route('users.index')->with('success', 'User updated successfully');
}
Enter fullscreen mode Exit fullscreen mode

Here, we add $user->syncRoles($request->role); to synchronize the roles on that user.

use Spatie\Permission\Models\Role;
Enter fullscreen mode Exit fullscreen mode

Don't forget to import the Role model in UserController.php

users/form.blade.php

<div class="mb-3">
    <label for="role" class="form-label">Role</label>
    <select class="form-select @error('role') is-invalid @enderror" id="role" name="role">
        <option value="">-- Select Role --</option>
        @foreach($roles as $role)
        <option value="{{ $role->name }}" {{ in_array($role->name, $user->roles->pluck('name')->toArray()) ? 'selected' : '' }}>{{ $role->name }}</option>
        @endforeach
    </select>
    @error('role')
    <div class="invalid-feedback">
        {{ $message }}
    </div>
    @enderror
</div>
Enter fullscreen mode Exit fullscreen mode

Next, open the users/form.blade.php file, then add a select field like the code above.

Role Management

Ok, we will create role management to manage the permissions of each role.

php artisan make:controller RoleController
Enter fullscreen mode Exit fullscreen mode

Let's create a RoleController. Please run the command as above to create a RoleController file.

RoleController

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;

class RoleController extends Controller
{
    public function index()
    {
        return view('roles.index',[
            'roles' => Role::paginate(10)->withQueryString()
        ]);
    }

    public function edit(Role $role)
    {
        return view('roles.form',[
            'role'        => $role,
            'permissions' => Permission::get(),
        ]);
    }

    public function update(Request $request, Role $role)
    {
        $role->syncPermissions($request->permissions);

        return to_route('roles.index')->with('success', 'Role updated successfully');
    }
}
Enter fullscreen mode Exit fullscreen mode

Open the RoleController file that we just created, then adjust the code in it to be like the code above.

routes/web.php

Route::prefix('roles')->group(function () {
    Route::get('/', [App\Http\Controllers\RoleController::class, 'index'])->name('roles.index');
    Route::get('/{role}/edit', [App\Http\Controllers\RoleController::class, 'edit'])->name('roles.edit');
    Route::put('/{role}', [App\Http\Controllers\RoleController::class, 'update'])->name('roles.update');
});
Enter fullscreen mode Exit fullscreen mode

Open routes/web.php, add a new route like the code above into the web.php file.

roles/index.blade.php

@extends('layouts.app')
@section('title', 'Roles')
@section('content')
<div class="container">
    <div class="row">
        <div class="col-12">
            <div class="card">
                <div class="card-header d-flex justify-content-between">
                    <h1 class="fs-5">Roles</h1>
                </div>
                <div class="card-body">
                    @if (session('success'))
                    <div class="alert alert-success" role="alert">
                        {{ session('success') }}
                    </div>
                    @endif
                    <table class="table">
                        <thead>
                            <tr>
                                <th scope="col">#</th>
                                <th scope="col">Name</th>
                                <th scope="col">Permissions</th>
                                <th scope="col" class="text-end">Action</th>
                            </tr>
                        </thead>
                        <tbody>
                            @forelse ($roles as $key => $role)
                            <tr>
                                <th scope="row">{{ ++$key }}</th>
                                <td>{{ $role->name }}</td>
                                <td>
                                    @forelse($role->permissions as $permission)
                                    <span class="badge bg-dark me-1">{{ $permission->name }}</span>
                                    @empty
                                    -
                                    @endforelse
                                </td>
                                <td class="text-end">
                                    <a href="{{ route('roles.edit', $role->id) }}" class="btn btn-sm btn-primary">Edit</a>
                                </td>
                            </tr>
                            @empty
                            <tr>
                                <td colspan="4">No roles found</td>
                            </tr>
                            @endforelse
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection
Enter fullscreen mode Exit fullscreen mode

Create a new folder inside the resources/views folder named roles and create an index.blade.php file inside the roles folder. Please copy the code above and paste it into the new file (index.blade.php).

roles/form.blade.php

@extends('layouts.app')
@section('title',"{$role->name}")
@section('content')
<div class="container">
    <div class="row d-flex justify-content-center">
        <div class="col-md-6">
            <div class="card">
                <div class="card-header">
                    <h1 class="fs-5">{{ $role->name }}</h1>
                </div>
                <div class="card-body">
                    <form action="{{ route('roles.update',$role->id) }}" method="post">
                        @method('PUT')
                        @csrf
                        <div class="row mb-2">
                            @foreach ($permissions as $permission)
                            <div class="col-md-4">
                                <div class="form-check">
                                    <input class="form-check-input" type="checkbox" value="{{ $permission->name }}" id="permission{{ $permission->id }}" name="permissions[]" {{ in_array($permission->id, $role->permissions->pluck('id')->toArray()) ? 'checked' : '' }}>
                                    <label class="form-check-label" for="permission{{ $permission->id }}">
                                        {{ $permission->name }}
                                    </label>
                                </div>
                            </div>
                            @endforeach
                        </div>
                        <button type="submit" class="btn btn-primary">
                            Update
                        </button>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection
Enter fullscreen mode Exit fullscreen mode

Then, create a new file named form.blade.php in the roles folder. Please copy the code above and paste it into the new file.

Next, we can add a new menu to the navbar.

<li class="nav-item">
        <a class="nav-link" href="{{ route('roles.index') }}">{{ __('Roles') }}</a>
</li>
Enter fullscreen mode Exit fullscreen mode

Open the app.blade.php file, then add the code above to the app.blade.php file. Place it under the Users menu that we added earlier.

Now, let's try role management. Run the project, then open it in a browser. The table and form will display like the image below.

laravel role permissions
laravel role permissions
We have successfully created role management up to this point. In the following explanation, let's try to implement it both with a blade directive and with middleware.

Basic Usage

Okay, let's try what we've created.

Blade Directive

First, let's try using the blade directive. Please open the layouts/app.blade.php file.

<!-- Left Side Of Navbar -->
<ul class="navbar-nav me-auto">
    @can('user table')
    <li class="nav-item">
        <a class="nav-link" href="{{ route('users.index') }}">{{ __('Users') }}</a>
    </li>
    @endcan
    @can('role table')
    <li class="nav-item">
        <a class="nav-link" href="{{ route('roles.index') }}">{{ __('Roles') }}</a>
    </li>
    @endcan
    @can('post table')
    <li class="nav-item">
        <a class="nav-link" href="#">{{ __('Posts') }}</a>
    </li>
    @endcan
</ul>
Enter fullscreen mode Exit fullscreen mode

In the menus section, please change it to be like the code above. With this code, it means that the Users menu can only be accessed by users or roles that have the "user table" permission. And the Roles menu can only be accessed by users or roles that have the "role table" permission. And the Posts menu, although we haven't created the management, we have created the permission, so we can try it by adding the Posts menu with the "post table" permission.

Now, you can try it by setting the permissions of each role (admin and editor). Then, try adding a user with a different role. Suppose you have set the editor role, which only has the following permissions: post table, create post, and edit post, and you add a new user with the editor role. Then, when you try to log in using the editor account, you should only see the Posts menu there.

Middleware

Okay, next, let's try using middleware. Laravel permission can be implemented using middleware in routes and controllers.

bootstrap/app.php

<?php

use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->alias([
            'role' => \Spatie\Permission\Middleware\RoleMiddleware::class,
            'permission' => \Spatie\Permission\Middleware\PermissionMiddleware::class,
            'role_or_permission' => \Spatie\Permission\Middleware\RoleOrPermissionMiddleware::class,
        ]);
    })
    ->withExceptions(function (Exceptions $exceptions) {
        //
    })->create();
Enter fullscreen mode Exit fullscreen mode

Please open the bootstrap/app.php file, then adjust the withMiddleware section to be like the example above. Here, we will register the default middleware from the Laravel permission package.

Let's try to implement middleware on routes.

Open the routes/web.php file and look for the code below.

Route::prefix('roles')->group(function () {
    Route::get('/', [App\Http\Controllers\RoleController::class, 'index'])->name('roles.index');
    Route::get('/{role}/edit', [App\Http\Controllers\RoleController::class, 'edit'])->name('roles.edit');
    Route::put('/{role}', [App\Http\Controllers\RoleController::class, 'update'])->name('roles.update');
});
Enter fullscreen mode Exit fullscreen mode

Then add middleware as below.

Route::prefix('roles')->group(function () {
    Route::get('/', [App\Http\Controllers\RoleController::class, 'index'])->name('roles.index')->middleware('can:role table');
    Route::get('/{role}/edit', [App\Http\Controllers\RoleController::class, 'edit'])->name('roles.edit')->middleware('can:edit role');
    Route::put('/{role}', [App\Http\Controllers\RoleController::class, 'update'])->name('roles.update')->middleware('can:edit role');
});
Enter fullscreen mode Exit fullscreen mode

With the code as above, it means that to be able to access /roles, the user or role must have "role table" permission. And to access the /{role}/edit route, the user or role must have "edit role" permission.

UserController.php

public function __construct()
{
    $this->middleware(['permission:user table'])->only('index');
    $this->middleware(['permission:create user'])->only(['create', 'store']);
    $this->middleware(['permission:edit user'])->only(['edit', 'update']);
}
Enter fullscreen mode Exit fullscreen mode

And finally, we can implement middleware on the controller. Please open the UserController.php file, then add the function __construct() as in the example above. The code above means that the index function can only be accessed by users or roles that have the "user table" permission. The create and store functions can only be accessed by users or roles that have the "create user" permission. The edit and update functions can only be accessed by users or roles that have the "edit user" permission.

Implementing advanced role and permission management in Laravel has never been easier, thanks to the Laravel Permission package. In this article, we’ve walked through the step-by-step process, starting with setting up a fresh Laravel project, installing the Laravel UI package for basic authentication, installing the Laravel Permission package, and finally implementing permissions using Blade directives and middleware.

By following this guide, you can ensure your application has robust access control while maintaining ease of management and scalability for future needs. A well-structured approach to roles and permissions not only enhances security but also provides flexibility to meet diverse user requirements.

We hope this tutorial helps you build more professional and secure applications. Feel free to experiment and explore the full potential of the Laravel Permission package to suit your project’s needs. Happy coding, and good luck!

Source Code: Laravel Permission

Top comments (0)