Laravel

Auth & Roles

Authentication setup with Breeze & Sanctum, role-based access control using Gates and Policies, middleware guards, and manual auth checks in controllers and Blade views.

1. Installation & Setup: Install spatie/laravel-permission. Publish migrations, jalankan migrate, dan WAJIB menambahkan trait HasRoles ke model User.
📄terminal & User.php
BASH/PHP
# 1. Install package
composer require spatie/laravel-permission

# 2. Publish config & migration
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"

# 3. Clear cache & Migrate
php artisan optimize:clear
php artisan migrate

# 4. Tambahkan Trait 'HasRoles' di model User
// app/Models/User.php
use Spatie\Permission\Traits\HasRoles;

class User extends Authenticatable
{
    use HasRoles; // <-- WAJIB TAMBAHKAN INI

    // ...
}
2. Create Roles & Permissions: Gunakan model Role dan Permission dari Spatie untuk membuat role baru dan memberikan hak akses (permission). Biasanya ini ditaruh di DatabaseSeeder.
📄Seeder / Controller
PHP
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;

// Membuat Permission
Permission::create(['name' => 'edit articles']);
Permission::create(['name' => 'delete articles']);
Permission::create(['name' => 'publish articles']);
Permission::create(['name' => 'unpublish articles']);

// Membuat Role
$writerRole = Role::create(['name' => 'writer']);
$adminRole = Role::create(['name' => 'admin']);

// Memberikan permission ke role
$writerRole->givePermissionTo('edit articles');
$writerRole->givePermissionTo('publish articles');

// Bisa juga pakai array
$adminRole->givePermissionTo([
    'edit articles', 
    'delete articles', 
    'publish articles', 
    'unpublish articles'
]);

// Atau pakai syncPermissions (akan menghapus yang tidak ada di list)
$writerRole->syncPermissions(['edit articles']);
3. Assign Roles ke User: Menggunakan instance model User (yang sudah dipasang Trait HasRoles), Anda bisa memberikan role, mencabut, atau mengeceknya.
📄Controller
PHP
$user = User::find(1);

// Memberikan role ke user
$user->assignRole('writer');
$user->assignRole(['writer', 'admin']); // multiple

// Menghapus role dari user
$user->removeRole('writer');

// Sinkronisasi role (menghapus yang lama, set yang baru)
$user->syncRoles(['super-admin']);

// Cek apakah user punya role tertentu (mengembalikan boolean)
$user->hasRole('writer');
$user->hasAnyRole(['writer', 'admin']);
$user->hasAllRoles(['writer', 'admin']);

// Cek permission langsung ke user (meskipun permission ada di role nya, akan return true)
$user->hasPermissionTo('edit articles');
$user->hasAnyPermission(['edit articles', 'delete articles']);

// Memberikan direct permission ke user (bypass role)
$user->givePermissionTo('edit articles');
4. Route Middleware: Spatie menyediakan middleware role, permission, dan role_or_permission untuk melindungi route. (Pastikan mendaftarkan array alias di bootstrap/app.php jika Laravel 11 membutuhkan alias).
📄routes/web.php
PHP
// Di Laravel 11, Anda bisa langsung pakai middleware string dari spatie
// Atur di routes/web.php

// 1. Cek berdasarkan Role
Route::group(['middleware' => ['role:super-admin']], function () {
    Route::get('/settings', [SettingController::class, 'index']);
});

// 2. Cek multiple Role (pakai pipe | )
Route::group(['middleware' => ['role:super-admin|admin']], function () {
    Route::get('/admin-panel', [AdminController::class, 'index']);
});

// 3. Cek berdasarkan Permission
Route::group(['middleware' => ['permission:publish articles']], function () {
    Route::get('/publish', [ArticleController::class, 'publish']);
});

// 4. Role atau Permission
Route::group(['middleware' => ['role_or_permission:super-admin|edit articles']], function () {
    Route::get('/edit', [ArticleController::class, 'edit']);
});
5. Blade Directives: Spatie menambahkan directives khusus seperti @role atau @hasanyrole untuk mengatur UI mana yang tampil berdasarkan role.
📄resources/views/...
BLADE
{{-- Cek Role --}}
@role('writer')
    I am a writer!
@else
    I am not a writer...
@endrole

{{-- Cek Multiple Roles --}}
@hasrole('writer|admin')
    I am either a writer or an admin!
@endhasrole

@hasanyrole($collectionOfRoles)
    I have one or more of these roles!
@endhasanyrole

@hasallroles('writer|admin')
    I am both a writer and an admin!
@endhasallroles

{{-- Cek Permission (Bisa pakai @can bawaan Laravel atau dari Spatie) --}}
@can('edit articles')
    <button>Edit Article</button>
@endcan

{{-- Cek kombinasi --}}
@unlessrole('does not have this role')
    I do not have the role
@endunlessrole
6. Controller Checks: Jika Anda butuh logika custom di Controller, gunakan hasRole() atau hasPermissionTo().
📄app/Http/Controllers/ArticleController.php
PHP
class ArticleController extends Controller
{
    public function update(Request $request, Article $article)
    {
        // 1. Menggunakan method can() bawaan user
        if ($request->user()->cannot('edit articles')) {
            abort(403);
        }

        // 2. Menggunakan method khusus Spatie
        if (! auth()->user()->hasRole('admin')) {
            abort(403, 'Unauthorized action.');
        }

        // 3. Melempar exception otomatis dari Spatie
        // Jika tidak punya permission, akan melempar Spatie\Permission\Exceptions\UnauthorizedException
        if (! auth()->user()->hasPermissionTo('edit articles')) {
            throw UnauthorizedException::forPermissions(['edit articles']);
        }
        
        // ...
    }
}
7. Super Admin Bypass: Daftarkan Gate::before di ServiceProvider agar role 'Super Admin' tidak perlu dicek lagi tiap permissionnya (otomatis allow semua).
📄app/Providers/AppServiceProvider.php
PHP
// app/Providers/AppServiceProvider.php

use Illuminate\Support\Facades\Gate;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        // Secara implisit mengabulkan (grant) semua permission kepada role Super Admin
        // Jadi tidak perlu mendaftarkan permission satu-satu untuk role ini.
        Gate::before(function ($user, $ability) {
            return $user->hasRole('Super Admin') ? true : null;
        });
    }
}