Laravel 12 Roles and Permissions (Without Any Packages) – A Complete Guide

Author

Kritim Yantra

Mar 27, 2025

Laravel 12 Roles and Permissions (Without Any Packages) – A Complete Guide

Managing user roles and permissions is essential for controlling access in web applications. While packages like Spatie Laravel-Permission exist, you can easily implement this in pure Laravel without external dependencies.

In this guide, we’ll cover:
Database Setup for Roles & Permissions
Assigning Roles to Users
Checking Permissions in Controllers & Views
Middleware for Role-Based Access

Let’s build a role-based system from scratch!


1. Why Build Roles & Permissions Without a Package?

🔹 Full control over the logic
🔹 No dependency on third-party packages
🔹 Lightweight & customizable
🔹 Great for learning Laravel’s core features


2. Database Structure

We’ll use 4 tables:

  1. users (Default Laravel users table)
  2. roles (Admin, Editor, User)
  3. permissions (create-post, edit-post, delete-post)
  4. role_permission (Pivot table linking roles and permissions)

Step 1: Create Migrations

Run these commands:

php artisan make:migration create_roles_table
php artisan make:migration create_permissions_table
php artisan make:migration create_role_permission_table

a) roles Table Migration

Schema::create('roles', function (Blueprint $table) {
    $table->id();
    $table->string('name'); // admin, editor, user
    $table->timestamps();
});

b) permissions Table Migration

Schema::create('permissions', function (Blueprint $table) {
    $table->id();
    $table->string('name'); // create-post, edit-post, delete-post
    $table->timestamps();
});

c) role_permission Pivot Table

Schema::create('role_permission', function (Blueprint $table) {
    $table->foreignId('role_id')->constrained();
    $table->foreignId('permission_id')->constrained();
    $table->primary(['role_id', 'permission_id']);
});

d) Add role_id to users Table

php artisan make:migration add_role_id_to_users_table
Schema::table('users', function (Blueprint $table) {
    $table->foreignId('role_id')->default(3)->constrained(); // Default role: 'user'
});

Run migrations:

php artisan migrate

3. Setting Up Models & Relationships

a) User Model

// app/Models/User.php
public function role()
{
    return $this->belongsTo(Role::class);
}

public function hasPermission($permissionName)
{
    return $this->role->permissions()->where('name', $permissionName)->exists();
}

b) Role Model

// app/Models/Role.php
public function permissions()
{
    return $this->belongsToMany(Permission::class);
}

c) Permission Model

// app/Models/Permission.php
public function roles()
{
    return $this->belongsToMany(Role::class);
}

4. Seeding Roles & Permissions

Let’s populate the database with sample data.

Create a Seeder

php artisan make:seeder RolePermissionSeeder
// database/seeders/RolePermissionSeeder.php
public function run()
{
    // Create Roles
    $admin = Role::create(['name' => 'admin']);
    $editor = Role::create(['name' => 'editor']);
    $user = Role::create(['name' => 'user']);

    // Create Permissions
    $createPost = Permission::create(['name' => 'create-post']);
    $editPost = Permission::create(['name' => 'edit-post']);
    $deletePost = Permission::create(['name' => 'delete-post']);

    // Assign Permissions to Roles
    $admin->permissions()->attach([$createPost->id, $editPost->id, $deletePost->id]);
    $editor->permissions()->attach([$createPost->id, $editPost->id]);
    $user->permissions()->attach([$createPost->id]);
}

Run the seeder:

php artisan db:seed --class=RolePermissionSeeder

5. Checking Permissions in Controllers

Example: Restrict Post Creation to Users with Permission

// app/Http/Controllers/PostController.php
public function create()
{
    if (!auth()->user()->hasPermission('create-post')) {
        abort(403, 'Unauthorized action.');
    }
    return view('posts.create');
}

6. Middleware for Role-Based Access

Step 1: Create Middleware

php artisan make:middleware CheckRole

Step 2: Define Logic

// app/Http/Middleware/CheckRole.php
public function handle($request, Closure $next, $role)
{
    if (auth()->check() && auth()->user()->role->name === $role) {
        return $next($request);
    }
    abort(403, 'Access Denied');
}

Step 3: Register Middleware

Add this tobootstrap/app.php:

->withMiddleware(function (Middleware $middleware) {
        $middleware->alias([
            'role' => \App\Http\Middleware\CheckRole::class,
        ]);
    })

Step 4: Protect Routes

Route::get('/admin/dashboard', function () {
    return view('admin.dashboard');
})->middleware('role:admin');

7. Checking Permissions in Blade Views

@if(auth()->user()->hasPermission('edit-post'))
    <a href="/posts/{{ $post->id }}/edit">Edit Post</a>
@endif

8. Assigning Roles to Users

// Make a user an admin
$user = User::find(1);
$user->role_id = 1; // admin role
$user->save();

9. Best Practices

Use caching for permissions to reduce database queries.
Avoid hardcoding role/permission names (use constants).
Test permissions thoroughly with unit tests.


Conclusion

You’ve just built a full role-permission system in Laravel without any packages!

🔹 Database Setup → Roles, Permissions, Pivot Tables
🔹 Models & RelationshipsUser, Role, Permission
🔹 Middleware & Controllers → Restrict access
🔹 Blade Checks → Show/hide UI elements

🚀 Now go ahead and implement this in your Laravel app!

Tags

Laravel Php

Comments

No comments yet. Be the first to comment!

Please log in to post a comment:

Continue with Google

Related Posts