Contoh Implementasi Policy dan Gate di Laravel 12: Studi Kasus CMS Multi-Role

Artikel ini melanjutkan penjelasan konsep Policy dan Gate di Laravel 12 dengan studi kasus implementasi lengkap: sistem manajemen konten dengan beberapa level akses.

Studi Kasus: Sistem CMS dengan Multi-Role

Skenario: aplikasi CMS dengan role admin, editor, dan author. Aturannya:

  • Admin bisa lakukan semua aksi di artikel mana saja
  • Editor bisa buat, edit, dan publish artikel mana saja
  • Author hanya bisa buat dan edit artikel miliknya sendiri

Setup Model User dengan Role

<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    protected $fillable = ['name', 'email', 'password', 'role'];

    public function isAdmin(): bool
    {
        return $this->role === 'admin';
    }

    public function isEditor(): bool
    {
        return in_array($this->role, ['admin', 'editor']);
    }
}

ArticlePolicy Lengkap

<?php

namespace App\Policies;

use App\Models\Article;
use App\Models\User;

class ArticlePolicy
{
    // Ini dijalankan sebelum semua method lain
    // Return true = admin bypass semua check
    public function before(User $user, string $ability): ?bool
    {
        if ($user->isAdmin()) {
            return true;
        }

        return null; // null = lanjut ke check berikutnya
    }

    public function viewAny(?User $user): bool
    {
        // Semua orang bisa lihat daftar artikel yang published
        return true;
    }

    public function view(?User $user, Article $article): bool
    {
        if ($article->status === 'published') {
            return true;
        }

        // Draft hanya bisa dilihat pemilik atau editor
        return $user && ($article->user_id === $user->id || $user->isEditor());
    }

    public function create(User $user): bool
    {
        return $user->hasVerifiedEmail();
    }

    public function update(User $user, Article $article): bool
    {
        // Editor bisa edit semua, author hanya punya sendiri
        return $user->isEditor() || $article->user_id === $user->id;
    }

    public function delete(User $user, Article $article): bool
    {
        // Hanya admin (via before()) atau pemilik artikel
        return $article->user_id === $user->id;
    }

    public function publish(User $user, Article $article): bool
    {
        // Hanya editor ke atas yang bisa publish
        return $user->isEditor();
    }

    public function restore(User $user, Article $article): bool
    {
        return $user->isEditor() || $article->user_id === $user->id;
    }
}

Menggunakan Policy di Controller

<?php

namespace App\Http\Controllers;

use App\Models\Article;
use App\Http\Requests\StoreArticleRequest;

class ArticleController extends Controller
{
    public function index()
    {
        $this->authorize('viewAny', Article::class);

        $articles = Article::with('author')
                           ->when(!auth()->user()?->isEditor(), fn ($q) =>
                               $q->where('status', 'published')
                                 ->orWhere('user_id', auth()->id())
                           )
                           ->paginate(15);

        return view('articles.index', compact('articles'));
    }

    public function edit(Article $article)
    {
        $this->authorize('update', $article);
        return view('articles.edit', compact('article'));
    }

    public function destroy(Article $article)
    {
        $this->authorize('delete', $article);
        $article->delete();
        return redirect()->route('articles.index')
                         ->with('success', 'Artikel dihapus.');
    }

    public function publish(Article $article)
    {
        $this->authorize('publish', $article);
        $article->update(['status' => 'published', 'published_at' => now()]);
        return back()->with('success', 'Artikel dipublish.');
    }
}

Policy di Blade Template

@foreach ($articles as $article)
    <div>
        <h2>{{ $article->title }}</h2>

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

        @can('publish', $article)
            @if($article->status === 'draft')
                <form action="{{ route('articles.publish', $article) }}" method="POST">
                    @csrf @method('PATCH')
                    <button>Publish</button>
                </form>
            @endif
        @endcan

        @can('delete', $article)
            <form action="{{ route('articles.destroy', $article) }}" method="POST">
                @csrf @method('DELETE')
                <button>Hapus</button>
            </form>
        @endcan
    </div>
@endforeach

Gate untuk Aksi Global

Untuk akses fitur yang tidak terkait model tertentu, pakai Gate:

// Di AppServiceProvider
Gate::define('access-analytics', fn (User $user) => $user->isEditor());
Gate::define('export-all-data',  fn (User $user) => $user->isAdmin());
// Di controller
Gate::authorize('access-analytics');
return view('analytics.dashboard');

// Di Blade
@can('access-analytics')
    <a href="/analytics">Analytics</a>
@endcan

Baca Juga

Butuh tim yang bantu implementasi sistem otorisasi yang tepat untuk aplikasi Laravel Anda? Lihat layanan pengembangan aplikasi kami.

Comments

Leave a Reply

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