Home Paket Belajar Bootcamp Instruktur

Tutorial Laravel Authorization #5 - Mini Project: Blog dengan Authorization Lengkap

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 34 kali

Rating: 5.00 ⭐

... 11 June 2026, 14:31

Di episode ini, kita rangkum semua materi dengan membangun sistem authorization lengkap untuk aplikasi blog sederhana:

  • Gate untuk proteksi halaman admin
  • Policy untuk manajemen postingan (CRUD)
  • Integrasi di controller, Blade, dan route middleware

Skenario Project

Kita punya aplikasi blog dengan dua jenis user:

Role Hak Akses
admin Bisa melakukan apa saja, termasuk akses panel admin
user (biasa) Bisa membuat postingan, dan mengedit/menghapus miliknya sendiri
Guest (belum login) Hanya bisa melihat postingan yang sudah published

Struktur File yang Dibuat

app/
├── Models/
│   ├── User.php
│   └── Post.php
├── Policies/
│   └── PostPolicy.php
├── Http/
│   └── Controllers/
│       ├── Admin/
│       │   └── DashboardController.php
│       └── PostController.php
└── Providers/
    └── AppServiceProvider.php

resources/views/
├── posts/
│   ├── index.blade.php
│   ├── show.blade.php
│   ├── create.blade.php
│   └── edit.blade.php
└── admin/
    └── dashboard.blade.php

Step 1 — Persiapan Model

Pastikan model User punya kolom role dan model Post punya kolom user_id dan status.

database/migrations/..._add_role_to_users_table.php:

Schema::table('users', function (Blueprint $table) {
    $table->string('role')->default('user'); // 'user' atau 'admin'
});

database/migrations/..._create_posts_table.php:

Schema::create('posts', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained()->cascadeOnDelete();
    $table->string('title');
    $table->text('content');
    $table->enum('status', ['draft', 'published'])->default('draft');
    $table->timestamps();
});

Step 2 — Definisikan Gate di AppServiceProvider

<?php

namespace App\Providers;

use Illuminate\Support\Facades\Gate;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        // Gate untuk akses halaman admin
        Gate::define('access-admin', function ($user) {
            return $user->role === 'admin';
        });
    }
}

Step 3 — Buat PostPolicy

php artisan make:policy PostPolicy --model=Post

app/Policies/PostPolicy.php:

<?php

namespace App\Policies;

use App\Models\Post;
use App\Models\User;

class PostPolicy
{
    // Admin bisa melakukan apa saja
    public function before(User $user, string $ability): bool|null
    {
        if ($user->role === 'admin') {
            return true;
        }
        return null;
    }

    // Guest bisa lihat postingan yang sudah published
    public function view(?User $user, Post $post): bool
    {
        if ($post->status === 'published') {
            return true;
        }
        // Jika draft, hanya pemilik yang boleh lihat
        return $user?->id === $post->user_id;
    }

    // Semua user yang login bisa buat postingan
    public function create(User $user): bool
    {
        return true;
    }

    // Hanya pemilik yang bisa edit
    public function update(User $user, Post $post): bool
    {
        return $user->id === $post->user_id;
    }

    // Hanya pemilik yang bisa hapus
    public function delete(User $user, Post $post): bool
    {
        return $user->id === $post->user_id;
    }
}

Step 4 — Setup Routes

routes/web.php:

use App\Http\Controllers\PostController;
use App\Http\Controllers\Admin\DashboardController;

// Route publik
Route::get('/posts', [PostController::class, 'index'])->name('posts.index');
Route::get('/posts/{post}', [PostController::class, 'show'])->name('posts.show');

// Route yang butuh login
Route::middleware('auth')->group(function () {
    Route::get('/posts/create', [PostController::class, 'create'])
        ->middleware('can:create,App\Models\Post')
        ->name('posts.create');

    Route::post('/posts', [PostController::class, 'store'])->name('posts.store');

    Route::get('/posts/{post}/edit', [PostController::class, 'edit'])
        ->middleware('can:update,post')
        ->name('posts.edit');

    Route::put('/posts/{post}', [PostController::class, 'update'])
        ->middleware('can:update,post')
        ->name('posts.update');

    Route::delete('/posts/{post}', [PostController::class, 'destroy'])
        ->middleware('can:delete,post')
        ->name('posts.destroy');
});

// Route admin
Route::middleware(['auth', 'can:access-admin'])->prefix('admin')->group(function () {
    Route::get('/dashboard', [DashboardController::class, 'index'])->name('admin.dashboard');
});

Step 5 — PostController

<?php

namespace App\Http\Controllers;

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

class PostController extends Controller
{
    public function index()
    {
        // Tampilkan semua postingan published untuk publik
        // + semua postingan milik user yang login
        $posts = Post::where('status', 'published')
            ->orWhere('user_id', auth()->id())
            ->latest()
            ->paginate(10);

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

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

    public function create()
    {
        return view('posts.create');
    }

    public function store(Request $request)
    {
        $this->authorize('create', Post::class);

        $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)
    {
        return view('posts.edit', compact('post'));
    }

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

        $post->update($validated);

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

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

Catatan: Authorization di editupdate, dan destroy sudah ditangani oleh middleware di route, jadi tidak perlu $this->authorize() lagi di controller. Ini salah satu keuntungan memakai middleware route.


Step 6 — Blade Template

resources/views/posts/index.blade.php:

<h1>Semua Postingan</h1>

@can('create', App\Models\Post::class)
    <a href="{{ route('posts.create') }}" class="btn btn-primary">+ Tulis Postingan</a>
@endcan

@foreach ($posts as $post)
    <div class="card mb-3">
        <h2>{{ $post->title }}</h2>
        <small>Oleh: {{ $post->user->name }} | Status: {{ $post->status }}</small>

        <div class="mt-2">
            <a href="{{ route('posts.show', $post) }}">Baca</a>

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

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

resources/views/layouts/navbar.blade.php (partial):

<nav>
    <a href="{{ route('posts.index') }}">Blog</a>

    @auth
        @can('access-admin')
            <a href="{{ route('admin.dashboard') }}">Admin Panel</a>
        @endcan

        <a href="{{ route('posts.create') }}">Tulis</a>
    @endauth
</nav>

Langkah Selanjutnya

Setelah memahami Gates dan Policies, teman-teman bisa melanjutkan ke:

  • Spatie Laravel Permission — package populer untuk role & permission yang lebih kompleks
  • Laravel Sanctum / Passport — authorization untuk API
  • Multi-tenancy — memisahkan data dan hak akses antar organisasi

Daftar eBook