Home Paket Belajar Bootcamp Instruktur

Tutorial Laravel Authorization #6 - Spatie Laravel Permission: Role & Permission yang Lebih Powerful

Pelajari sistem Authorization di Laravel dari nol hingga implementasi dalam studi kasus nyata melalui 5 episode terstruktur. Ebook ini membahas konsep Authentication vs Authorization, Gates, Policies, fitur lanjutan, hingga menggabungkan seluruh konsep dalam sebuah aplikasi blog. Setiap materi dilengkapi penjelasan konsep, contoh kode siap pakai, tabel perbandingan, dan praktik terbaik agar mudah dipahami oleh developer Laravel pemula maupun menengah.

✅ Telah dilihat 30 kali

Rating: 5.00 ⭐

... 11 June 2026, 14:31

Setelah membaca episode ini, teman-teman diharapkan bisa:

  • Memahami kapan Gates/Policies sudah tidak cukup dan kenapa Spatie dibutuhkan
  • Menginstall dan mengkonfigurasi spatie/laravel-permission di Laravel 11
  • Membuat roles dan permissions via seeder
  • Menetapkan role ke user dan menggunakannya di controller, Blade, dan middleware

1. Kenapa Tidak Cukup Pakai Gates & Policies?

Gates dan Policies dari episode sebelumnya sudah bagus untuk aplikasi sederhana. Tapi bayangkan skenario ini:

  • teman-teman punya 10 role berbeda (admin, editor, moderator, viewer, dsb.)
  • Role bisa berubah sewaktu-waktu tanpa deploy ulang
  • Satu user bisa punya lebih dari satu role
  • Permission perlu dikelola dari halaman admin oleh non-developer

Dengan Gates/Policies murni, semua aturan hardcode di PHP. Spatie menyimpan role & permission di database, sehingga bisa diubah secara dinamis.

Fitur Gates/Policies Spatie
Disimpan di File PHP Database
Bisa diubah tanpa deploy
Multi-role per user Manual ✅ Bawaan
Manajemen dari UI admin Susah ✅ Mudah
Cocok untuk Aturan sederhana/fixed Role & permission dinamis

2. Instalasi & Konfigurasi

Install Package

composer require spatie/laravel-permission

Publish Config & Migration

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

Ini akan membuat dua file:

  • config/permission.php — konfigurasi package
  • database/migrations/..._create_permission_tables.php — tabel-tabel yang dibutuhkan

Jalankan Migration

php artisan migrate

Tabel yang dibuat otomatis:

roles               — menyimpan daftar role
permissions         — menyimpan daftar permission
model_has_roles     — relasi user ↔ role
model_has_permissions — relasi user ↔ permission langsung
role_has_permissions  — relasi role ↔ permission

Tambahkan Trait ke Model User

<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Spatie\Permission\Traits\HasRoles; // ← tambahkan ini

class User extends Authenticatable
{
    use HasRoles; // ← tambahkan ini

    // ... kode lainnya
}

Reset Cache (Penting!)

Setiap kali mengubah role/permission, jalankan:

php artisan permission:cache-reset

3. Membuat Roles & Permissions via Seeder

Cara terbaik membuat data awal role dan permission adalah via seeder, bukan manual dari tinker.

php artisan make:seeder RolePermissionSeeder

database/seeders/RolePermissionSeeder.php:

<?php

namespace Database\Seeders;

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

class RolePermissionSeeder extends Seeder
{
    public function run(): void
    {
        // Reset cache dulu sebelum seeding
        app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions();

        // 1. Buat semua permissions
        $permissions = [
            // Post
            'post.viewAny',
            'post.view',
            'post.create',
            'post.update',
            'post.delete',

            // User management
            'user.viewAny',
            'user.create',
            'user.update',
            'user.delete',

            // Settings
            'settings.manage',
        ];

        foreach ($permissions as $permission) {
            Permission::firstOrCreate(['name' => $permission]);
        }

        // 2. Buat roles dan tetapkan permissions

        // Admin — akses penuh
        $adminRole = Role::firstOrCreate(['name' => 'admin']);
        $adminRole->givePermissionTo(Permission::all());

        // Editor — kelola konten, tidak bisa kelola user
        $editorRole = Role::firstOrCreate(['name' => 'editor']);
        $editorRole->givePermissionTo([
            'post.viewAny',
            'post.view',
            'post.create',
            'post.update',
            'post.delete',
        ]);

        // Viewer — hanya bisa lihat
        $viewerRole = Role::firstOrCreate(['name' => 'viewer']);
        $viewerRole->givePermissionTo([
            'post.viewAny',
            'post.view',
        ]);
    }
}

Daftarkan di DatabaseSeeder:

// database/seeders/DatabaseSeeder.php
public function run(): void
{
    $this->call(RolePermissionSeeder::class);

    // Buat user admin untuk testing
    $admin = \App\Models\User::firstOrCreate(
        ['email' => '[email protected]'],
        ['name' => 'Admin', 'password' => bcrypt('password')]
    );
    $admin->assignRole('admin');

    $editor = \App\Models\User::firstOrCreate(
        ['email' => '[email protected]'],
        ['name' => 'Editor', 'password' => bcrypt('password')]
    );
    $editor->assignRole('editor');
}

Jalankan seeder:

php artisan db:seed

4. Operasi Dasar: Assign, Check, Revoke

Menetapkan Role ke User

$user = User::find(1);

// Tetapkan satu role
$user->assignRole('editor');

// Tetapkan beberapa role sekaligus
$user->assignRole(['editor', 'viewer']);

// Atau pakai syncRoles — hapus role lama, ganti dengan yang baru
$user->syncRoles(['admin']);

Mengecek Role User

$user->hasRole('admin');           // true/false
$user->hasRole(['admin', 'editor']); // true jika punya salah satu
$user->hasAllRoles(['admin', 'editor']); // true jika punya keduanya

Mengecek Permission

// Cek permission langsung
$user->can('post.create');         // via Laravel helper
$user->hasPermissionTo('post.create'); // via Spatie method

// Cek dari role yang dimiliki
$user->hasRole('editor'); // editor punya post.create → true

Mencabut Role

$user->removeRole('editor');

5. Menggunakan di Controller

<?php

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\Request;

class PostController extends Controller
{
    public function index()
    {
        // Cara 1: pakai can() — sama seperti sebelumnya
        if (!auth()->user()->can('post.viewAny')) {
            abort(403);
        }

        $posts = Post::latest()->paginate(10);
        return view('posts.index', compact('posts'));
    }

    public function create()
    {
        // Cara 2: authorize() — lempar 403 otomatis
        $this->authorize('post.create');

        return view('posts.create');
    }

    public function store(Request $request)
    {
        $this->authorize('post.create');

        $validated = $request->validate([
            'title'   => 'required|string|max:255',
            'content' => 'required|string',
            'status'  => 'required|in:draft,published',
        ]);

        auth()->user()->posts()->create($validated);

        return redirect()->route('posts.index')->with('success', 'Postingan berhasil dibuat.');
    }

    public function edit(Post $post)
    {
        $this->authorize('post.update');
        return view('posts.edit', compact('post'));
    }

    public function update(Request $request, Post $post)
    {
        $this->authorize('post.update');

        $post->update($request->validate([
            'title'   => 'required|string|max:255',
            'content' => 'required|string',
            'status'  => 'required|in:draft,published',
        ]));

        return redirect()->route('posts.show', $post)->with('success', 'Postingan diperbarui.');
    }

    public function destroy(Post $post)
    {
        $this->authorize('post.delete');
        $post->delete();
        return redirect()->route('posts.index')->with('success', 'Postingan dihapus.');
    }
}

6. Menggunakan di Blade Template

Spatie kompatibel penuh dengan direktif @can bawaan Laravel:

{{-- Cek permission --}}
@can('post.create')
    <a href="{{ route('posts.create') }}" class="btn btn-primary">+ Tulis Postingan</a>
@endcan

{{-- Cek role --}}
@role('admin')
    <a href="{{ route('admin.dashboard') }}">Admin Panel</a>
@endrole

{{-- Cek salah satu dari beberapa role --}}
@hasanyrole('admin|editor')
    <span class="badge">Staff</span>
@endhasanyrole

{{-- Kombinasi --}}
@foreach ($posts as $post)
    <div class="card">
        <h2>{{ $post->title }}</h2>

        @can('post.update')
            <a href="{{ route('posts.edit', $post) }}">Edit</a>
        @endcan

        @can('post.delete')
            <form action="{{ route('posts.destroy', $post) }}" method="POST" style="display:inline">
                @csrf @method('DELETE')
                <button type="submit">Hapus</button>
            </form>
        @endcan
    </div>
@endforeach

Direktif khusus Spatie: @role@hasrole@hasanyrole@hasallroles@unlessrole — semua tersedia setelah install package.


7. Proteksi Route via Middleware

Spatie otomatis mendaftarkan dua middleware:

Middleware Fungsi
role:admin Cek apakah user punya role tertentu
permission:post.create Cek apakah user punya permission tertentu

Penggunaan di routes/web.php:

// Proteksi berdasarkan role
Route::middleware(['auth', 'role:admin'])->prefix('admin')->group(function () {
    Route::get('/dashboard', [Admin\DashboardController::class, 'index'])->name('admin.dashboard');
    Route::resource('users', Admin\UserController::class);
});

// Proteksi berdasarkan permission
Route::middleware(['auth', 'permission:post.create'])->group(function () {
    Route::get('/posts/create', [PostController::class, 'create'])->name('posts.create');
    Route::post('/posts', [PostController::class, 'store'])->name('posts.store');
});

// Beberapa role sekaligus (pakai pipe)
Route::middleware(['auth', 'role:admin|editor'])->group(function () {
    Route::get('/posts/{post}/edit', [PostController::class, 'edit'])->name('posts.edit');
    Route::put('/posts/{post}', [PostController::class, 'update'])->name('posts.update');
});

8. Migrasi dari Gates/Policies ke Spatie

Jika teman-teman sudah punya Gates/Policies dari episode sebelumnya dan ingin beralih ke Spatie, keduanya bisa berjalan berdampingan. Tidak perlu hapus semua sekaligus.

Strategi migrasi bertahap:

// AppServiceProvider.php
// Gate lama tetap bisa dipakai
Gate::define('access-admin', function ($user) {
    return $user->hasRole('admin'); // ← ganti kondisi menjadi cek role Spatie
});
// PostPolicy.php — tetap bisa dipakai, tapi kondisinya diganti
public function update(User $user, Post $post): bool
{
    // Dulu: return $user->id === $post->user_id;
    // Sekarang: admin bisa edit semua, editor hanya miliknya
    if ($user->hasRole('admin')) return true;
    return $user->id === $post->user_id && $user->can('post.update');
}

Ringkasan Episode

  • Spatie menyimpan role & permission di database — bisa diubah tanpa deploy ulang
  • Install dengan composer require spatie/laravel-permission, tambahkan trait HasRoles ke model User
  • Buat roles & permissions via seeder agar reproducible
  • Gunakan assignRole()hasRole()can()hasPermissionTo() di kode PHP
  • Di Blade: @can@role@hasanyrole — semua tersedia
  • Di route: middleware role:admin atau permission:post.create
  • Bisa berjalan berdampingan dengan Gates/Policies yang sudah ada

Preview Episode Berikutnya

Di Episode 7, kita akan membangun halaman manajemen role & permission dari UI admin — sehingga admin bisa menambah, mengubah, dan mencabut role/permission langsung dari browser tanpa perlu menyentuh kode.

Daftar eBook