• Home
  • Blogs
  • Laravel Roles and Permissions Using Spatie
Blog Details

Laravel Roles and Permissions Using Spatie

If you’re working on an admin panel in Laravel and want to restrict user access based on roles (like Admin, Editor, or Author), then the Spatie Laravel Permission Package is your best friend.

In this guide, we’ll build a complete Roles and Permissions Management System from scratch using Spatie, including:

  • Role and permission creation,

  • Assigning permissions to roles,

  • Assigning roles to users,

  • And controlling page access dynamically using middleware and guards.

Let’s dive in step by step 👇


🧠 What is Spatie Laravel Permission?

Spatie Laravel Permission is a package that lets you assign roles and permissions to users easily.
It integrates deeply with Laravel’s authorization system, meaning you can restrict routes, views, or even buttons in your Blade templates.


⚙️ Step 1: Install Spatie Laravel Permission

Run this command in your terminal:

composer require spatie/laravel-permission

Then publish the configuration and migration files:

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

This will create:

  • config/permission.php

  • And database migration files for roles, permissions, and model_has_roles tables.


🗃 Step 2: Run the Migrations

Now run:

php artisan migrate

This will create all the necessary tables for managing roles and permissions.


🧱 Step 3: Add group_name Column to Permissions Table

To organize permissions better (like grouping them into “CMS Panel”, “Settings”, etc.),
we’ll add a new column group_name to the permissions table.

Run:

php artisan make:migration add_group_name_to_permissions_table --table=permissions

In the migration file:

public function up()
{
    Schema::table('permissions', function (Blueprint $table) {
        $table->string('group_name')->nullable();
    });
}

Then migrate:

php artisan migrate

👤 Step 4: Update Admin Model

We’ll use the HasRoles trait provided by Spatie to assign roles and permissions to our admin users.

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Spatie\Permission\Traits\HasRoles;

class Admin extends Authenticatable
{
    use HasRoles;

    protected $guard = 'admin';
}

🧩 Step 5: Create Role CRUD

You can now build a simple Role CRUD.
Here’s an example of the store method in your RoleController:

public function store(Request $request)
{
    $request->validate([
        'name' => 'required',
    ]);

    Role::create([
        'name' => strtolower(str_replace('_', ' ', $request->name)),
        'guard_name' => 'admin',
    ]);

    toastr()->success('Role created successfully');
    return redirect(route('admin.user_roles.index'));
}

Explanation:

  • The form validates that a role name is entered.

  • Converts underscores to spaces and makes it lowercase.

  • Assigns it to the admin guard.

  • Shows a success notification using toastr.


⚡ Step 6: Create Permissions CRUD

Now, let’s make a permission management form where you can store permission name and group name.

Example form field (Blade file):

<div class="form-group mb-0">
    <label class="form-label">Group Name*</label>
    <div class="form-control-wrap">
        <select class="form-select js-select2" name="group_name" data-search="on">
            <option value="" selected disabled>Select One</option>
            <option value="cms_panel">CMS Panel</option>
            <option value="cms_category">CMS Category</option>
            <option value="cms_pages">CMS Pages</option>
            <option value="cms_blogs">CMS Blogs</option>
            <option value="web_pages">Web Pages</option>
            <option value="admin_users">Admin Users</option>
            <option value="settings">Settings</option>
            <option value="roles_permissions">Roles & Permissions</option>
        </select>
    </div>
</div>

Controller method:

public function store_permissions(Request $request)
{
    $request->validate([
        'name' => 'required',
        'group_name' => 'required',
    ]);

    Permission::create([
        'name' => $request->name,
        'group_name' => $request->group_name,
        'guard_name' => 'admin',
    ]);

    toastr()->success('Permission created successfully');
    return redirect(route('all.permission'));
}

Explanation:

  • Each permission is linked to a specific group_name for easier organization.

  • Guard name ensures it belongs to the admin panel.


🧠 Step 7: Combine Roles and Permissions (Assign Permissions to Roles)

Now we’ll build a system to assign multiple permissions to a role.

First, create some helper methods in your Admin model:

public static function getPermissionGroups()
{
    return DB::table('permissions')
        ->select('group_name')
        ->groupBy('group_name')
        ->get();
}

public static function getPermissionByGroupName($group_name)
{
    return DB::table('permissions')
        ->select('name', 'id')
        ->where('group_name', $group_name)
        ->get();
}

public static function roleHasPermissions($role, $permissions)
{
    foreach ($permissions as $permission) {
        if (!$role->hasPermissionTo($permission->name)) {
            return false;
        }
    }
    return true;
}

Explanation:

  • getPermissionGroups() fetches unique group names.

  • getPermissionByGroupName() gets all permissions belonging to that group.

  • roleHasPermissions() checks if a role already has all permissions in a group.


Controller: Sending Data to View

public function all_roles_permissions()
{
    $data['roles'] = Role::all();
    $data['permission_groups'] = Admin::getPermissionGroups();
    return view('adminpanel.adminuser.roles_permissions.add', $data);
}

Blade: Assign Permissions Form

In the Blade view, display groups and permissions:

<form method="post" action="{{ route('store.roles.permissions') }}">
    @csrf
    <div class="form-group">
        <label>Select Role*</label>
        <select name="role_id" class="form-select js-select2">
            <option value="" selected disabled>Select One</option>
            @foreach ($roles as $role)
                <option value="{{ $role->id }}">{{ $role->name }}</option>
            @endforeach
        </select>
    </div>

    <div class="custom-control custom-checkbox">
        <input type="checkbox" class="custom-control-input" id="checkDefaultmain">
        <label class="custom-control-label" for="checkDefaultmain">All Permissions</label>
    </div>

    <hr>

    @foreach ($permission_groups as $group)
        <div class="row">
            <div class="col-3">
                <label>{{ $group->group_name }}</label>
            </div>
            <div class="col-9">
                @php
                    $permissions = App\Models\Admin::getPermissionByGroupName($group->group_name);
                @endphp
                @foreach ($permissions as $perm)
                    <div class="custom-control custom-checkbox">
                        <input type="checkbox" name="permission[]" value="{{ $perm->id }}" class="permission-checkbox" data-group-id="{{ $group->group_name }}">
                        <label>{{ $perm->name }}</label>
                    </div>
                @endforeach
            </div>
        </div>
    @endforeach

    <button type="submit" class="btn btn-primary">Assign Permissions</button>
</form>

JavaScript for Checkbox Logic

 
$(document).ready(function() {
    $("#checkDefaultmain").on("click", function() {
        $(".custom-control-input").not(this).prop("checked", $(this).prop("checked"));
    });

    $(".group-checkbox").on("click", function() {
        let groupId = $(this).data("group-id");
        $(".permission-checkbox[data-group-id='" + groupId + "']")
            .prop("checked", $(this).prop("checked"));
    });
});

Explanation:

  • “All Permissions” checkbox selects or deselects all.

  • Group checkboxes handle all permissions in one group.


Roles With Permissions CRUD

  // Roles and Permissions

    public function all_roles_permissions()
    {
        $data['roles'] = Role::all();
        $data['permission_groups'] = Admin::getPermissionGroups();
        return view('adminpanel.adminuser.roles_permissions.add', $data);
    }

    public function store_roles_permissions(Request $request)
    {
        try {
            $request->validate([
                'role_id' => 'required',
                'permission' => 'required|array', // Ensure permission is an array
                'permission.*' => 'exists:permissions,id', // Validate each permission in the array
            ]);

            $role_id = $request->role_id;
            $permissions = $request->permission;

            // Check if the role already has permissions
            $existingPermissions = DB::table('role_has_permissions')
                ->where('role_id', $role_id)
                ->pluck('permission_id')
                ->toArray();

            // Compare the existing permissions with the ones being added
            $conflictingPermissions = array_intersect($permissions, $existingPermissions);

            if (!empty($conflictingPermissions)) {
                toastr()->error('The selected Role already has some permissions! Kindly Update');
            } else {
                foreach ($permissions as $permission_id) {
                    // Insert the new entry
                    DB::table('role_has_permissions')->insert([
                        'role_id' => $role_id,
                        'permission_id' => $permission_id,
                    ]);
                }
                toastr()->Success('Permissions assigned to the selected role Successfully');
            }
            return redirect()->route('show.roles.permissions');
        } catch (\Throwable $th) {
            toastr()->error($th->getMessage());
            return redirect()->back();
        }
    }


    public function show_roles_permissions()
    {
        $data['all_roles'] = Role::all();
        return view('adminpanel.adminuser.roles_permissions.show_roles_permissions', $data);
    }

    public function edit_roles_permissions($id)
    {
        $data['role'] = Role::findOrFail($id);
        $data['permissions'] = Permission::all();
        $data['permission_groups'] = Admin::getPermissionGroups();
        return view('adminpanel.adminuser.roles_permissions.edit', $data);
    }

    public function update_roles_permissions(request $request, $id)
    {
        // dd($request->all());
        $role = Role::findOrFail($id);
        $permissions = $request->permission;

        if (!empty($permissions)) {
            $role->syncPermissions($permissions);
        }
        toastr()->success('Permissions updated Successfully');
        return redirect()->route('show.roles.permissions');
    }

    public function delete_roles_permissions($id)
    {
        $role = Role::findOrFail($id);
        if (!is_null($role)) {
            $role->delete();
        }
        toastr()->success('Permissions Deleted Successfully');
        return redirect()->back();
    }
}

👨‍💼 Step 8: Assign Roles to Admin Users

In your AdminUserController:

public function add()
{
    $data['roles'] = Role::all();
    return view('adminpanel.adminuser.add', $data);
}

public function store(Request $request)
{
    $request->validate([
        'first_name' => 'required|max:30',
        'last_name' => 'required|max:30',
        'username' => 'required|unique:admins|max:30',
        'email' => 'required|email|unique:admins',
        'password' => 'required|min:8|confirmed',
        'role' => 'required',
    ]);

    $admin = new Admin();
    $admin->first_name = $request->first_name;
    $admin->last_name = $request->last_name;
    $admin->email = $request->email;
    $admin->username = $request->username;
    $admin->password = Hash::make($request->password);
    $admin->save();

    $role = Role::find($request->role);
    $admin->assignRole($role);

    toastr()->success('Admin user created and role assigned successfully!');
    return redirect()->route('admin.user.show');
}

Explanation:

  • Each admin is assigned a specific role at creation.

  • This role automatically gives them access to assigned permissions.


🔐 Step 9: Restrict Page Access with Middleware

You can now protect routes using Spatie’s middleware.

In your routes/admin.php:

Route::get('/show', [AdminUserController::class, 'show'])
    ->name('admin.user.show')
    ->middleware('checkPermission:show_admin_user');

✅ This ensures only users with the show_admin_user permission can view this route.


🧭 Step 10: Blade Conditional Access

In your Blade templates, you can restrict UI elements using permission checks:

@if (Auth::guard('admin')->user()->can('access_cms_panel'))
    <li class="nk-menu-item">
        <a href="{{ route('cms.dashboard') }}" class="nk-menu-link" title="CMS Panel">
            <span class="nk-menu-icon"><em class="icon ni ni-template"></em></span>
        </a>
    </li>
@endif

If the admin has the access_cms_panel permission, they’ll see this menu — otherwise, it stays hidden.

Conclusion

You’ve now built a complete Roles and Permissions Management System in Laravel using Spatie!
Your admin panel is now secure, modular, and easy to maintain.

With this setup, you can:

  • Add new roles anytime,

  • Assign specific permissions easily,

  • Protect routes, menus, and actions dynamically.

Leave A Reply

Your email address will not be published. Required fields are marked

Ahmad

Ahmad Raza

Hi, I'm Ahmad Raza — a passionate Web Developer and Laravel enthusiast.