← Back to list

Laravel 12 User Roles and Permissions with a Spatie package

Role-based access control (RBAC) is essential for most web applications. In this tutorial, I’ll show you how to implement a robust…

Dev Talk · 2025-08-17 05:42 · 1 claps · 3.0 min read paywalled
#laravel #laravel-framework #laravel-development #spatie #php
Open on Medium ↗
Wiki topics: 🌐 · Web Development

Laravel 12 User Roles and Permissions with a Spatie package

Role-based access control (RBAC) is essential for most web applications. In this tutorial, I’ll show you how to implement a robust permission system in Laravel 12 using the popular Spatie Laravel-Permission package.

Spatie Role Permission

Spatie Role Permission

Why Spatie Permissions?

The Spatie Laravel-Permission package provides:

  • Simple role and permission management
  • Middleware protection for routes
  • Blade directives for view protection
  • Database caching for optimal performance
  • Clean, maintainable code structure

Step 1: Install Spatie Laravel-Permission your project

To install the Spatie Laravel Permission (ACL) package, you’ll need to run the following commands in your terminal inside your Laravel project:

composer require spatie/laravel-permission

Once you install the Spatie Laravel Permission package with the version you need, you can publish its config and migration files so you can customize them. Run the following command in your terminal:

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

After publishing, you’ll see the config/permission.php file and the migration file inside database/migrations/. Now, to create the necessary tables for roles and permissions, just run:

php artisan migrate

Step 2: Set Up the User Model

To enable role & permission management on your User model, you need to add the HasRoles trait from Spatie.

Open your app/Models/User.php file and update it like this:

<?php

namespace App\Models;

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

class User extends Authenticatable
{
    use HasRoles;

    // ... rest of your model code
}

You just need to alias them in bootstrap/app.php (since Laravel 11+ doesn’t use the old Kernel.php for middleware registration).

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

3. Create Roles & Permissions (Seeder Example)

<?php

use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;

class RolePermissionSeeder extends Seeder
{
    public function run(): void
    {
        Permission::create(['name' => 'view articles']);
        Permission::create(['name' => 'create articles']);
        Permission::create(['name' => 'edit articles']);
        Permission::create(['name' => 'delete articles']);

        // Create roles and assign permissions
        $writer = Role::create(['name' => 'writer']);
        $writer->givePermissionTo(['view articles', 'create articles', 'edit articles']);

        $admin = Role::create(['name' => 'admin']);
        $admin->givePermissionTo(Permission::all());
    }
}

Run seeder:

php artisan db:seed --class=RolePermissionSeeder

4. Assign Roles to Users

$user = User::find(1);
$user->assignRole('admin');

$writer = User::find(2);
$writer->assignRole('writer');

5. Protect Routes with Middleware

Route::middleware(['role:admin'])->group(function () {
    Route::get('/admin/dashboard', fn() => 'Admin Dashboard');
});

Route::get('/articles/create', fn() => 'Create Article Page')
    ->middleware('permission:create articles');

6. Blade Protection With can function

Because all permissions will be registered on Laravel’s gate, you can check if a user has a permission with Laravel’s default can function:

@can('edit articles')
    <button>Edit</button>
@endcan

7. FormRequest Authorization (Best Practice)

Instead of checking permissions in controllers, use FormRequest classes.

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class CreateArticleRequest extends FormRequest
{
    public function authorize(): bool
    {
        // Only allow if user has "create articles" permission
        return $this->user()?->can('create articles');
    }

    public function rules(): array
    {
        return [
            'title' => 'required|string|max:255',
            'content' => 'required|string',
        ];
    }
}

Example: UpdateArticleRequest

<?php 

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class UpdateArticleRequest extends FormRequest
{
    public function authorize(): bool
    {
        // Allow only if user can "edit articles"
        return $this->user()?->can('edit articles');
    }

    public function rules(): array
    {
        return [
            'title' => 'sometimes|required|string|max:255',
            'content' => 'sometimes|required|string',
        ];
    }
}

Using in Controller

<?php

namespace App\Http\Controllers;

use App\Models\Article;
use App\Http\Requests\CreateArticleRequest;
use App\Http\Requests\UpdateArticleRequest;

class ArticleController extends Controller
{
    public function store(CreateArticleRequest $request)
    {
        Article::create($request->validated());
        return back()->with('success', 'Article created!');
    }

    public function update(UpdateArticleRequest $request, Article $article)
    {
        $article->update($request->validated());
        return back()->with('success', 'Article updated!');
    }
}

If the user does not have the required permission, Laravel automatically throws 403 Unauthorized before entering the controller.

Conclusion

Spatie’s Laravel Permission package gives you a clean and flexible way to handle role-based access control (RBAC) in your application. By combining middleware, Blade directives, and FormRequest authorization, you can protect your routes, controllers, and views with minimal effort.

  • Middleware ensures routes are secure.
  • Blade directives ensure the UI only shows what’s allowed.
  • FormRequests keep controllers clean while enforcing permissions automatically.

With this setup, your app follows best practices in authorization and stays scalable as your roles and permissions grow. Whether you’re building a small blog or a large enterprise system, this approach ensures your application remains secure, maintainable, and easy to extend.

If you found this helpful, feel free to share or drop a comment. Happy coding with Laravel! 🧱✨

Read More Article


메타데이터
post_id
12510d0f97aa
slug
laravel-12-user-roles-and-permissions-with-a-spatie-package-12510d0f97aa
url
https://medium.com/@devtalk94/laravel-12-user-roles-and-permissions-with-a-spatie-package-12510d0f97aa
canonical_url
https://medium.com/@devtalk94/laravel-12-user-roles-and-permissions-with-a-spatie-package-12510d0f97aa
author_url
https://medium.com/@devtalk94
status
ok
fetched_at
2026-07-18 04:29:11