Proyek Mini: Blog Lengkap
Project capstone: blog dengan auth, kategori, tag, komentar, upload cover, dan policy lengkap.
Arsitektur project
Project penutup ini menggabungkan hampir semua modul: Breeze untuk auth, relasi one-to-many (user-post, post-komentar) dan many-to-many (post-tag), upload cover, policy, pagination, dan seeder. Hasil akhirnya blog multi-user yang layak dipamerkan di portfolio.
Struktur tabel: users, posts (judul, slug, isi, cover, status, user_id, kategori_id), kategoris, komentars (post_id, user_id, isi), dan pivot post_tag.
Langkah 1: model dan relasi
php artisan make:model Post -mfs --policy
php artisan make:model Komentar -m
php artisan make:model Kategori -m
php artisan make:model Tag -mFlag -mfs membuat migration, factory, seeder sekaligus, dan --policy membuat policy-nya. Definisikan relasi:
// app/Models/Post.php
public function user(): BelongsTo { return $this->belongsTo(User::class); }
public function kategori(): BelongsTo { return $this->belongsTo(Kategori::class); }
public function komentars(): HasMany { return $this->komentars()->latest(); }
public function tags(): BelongsToMany { return $this->belongsToMany(Tag::class); }Langkah 2: controller dengan eager load
// app/Http/Controllers/PostController.php
public function index(Request $request)
{
$posts = Post::with(['user', 'kategori', 'tags'])
->withCount('komentars')
->where('status', 'published')
->when($request->kategori, fn ($q) => $q->whereHas('kategori',
fn ($q) => $q->where('slug', $request->kategori)))
->latest()
->paginate(9)
->withQueryString();
return view('posts.index', compact('posts'));
}
public function store(StorePostRequest $request)
{
$data = $request->validated();
$data['user_id'] = $request->user()->id;
if ($request->hasFile('cover')) {
$data['cover'] = $request->file('cover')->store('covers', 'public');
}
$post = Post::create($data);
$post->tags()->sync($request->input('tags', []));
return redirect()->route('posts.show', $post);
}Langkah 3: komentar dan policy
// app/Http/Controllers/KomentarController.php
public function store(Request $request, Post $post)
{
$validated = $request->validate(['isi' => 'required|string|max:1000']);
$post->komentars()->create([
'isi' => $validated['isi'],
'user_id' => $request->user()->id,
]);
return back()->with('sukses', 'Komentar terkirim!');
}
public function destroy(Komentar $komentar)
{
$this->authorize('delete', $komentar);
$komentar->delete();
return back();
}Policy komentar: pemilik komentar atau pemilik post boleh hapus.
Langkah 4: seed dan verifikasi
php artisan migrate:fresh --seed
php artisan testBuat seeder yang menghasilkan 5 user, tiap user 8 post dengan 2-4 tag acak dan beberapa komentar. Lalu tulis minimal 3 feature test: tamu tidak bisa posting, pemilik bisa edit postingnya sendiri, user lain tidak bisa edit posting orang.
Catatan teknis: Project ini adalah portfolio piece yang kuat. Sebelum dipamerkan, cek ulang: tidak ada N+1 (pakai Debugbar), semua route tulis terproteksi auth + policy, file upload tervalidasi, dan
APP_DEBUG=falsekalau di-deploy. Blog sederhana yang aman dan cepat jauh lebih mengesankan daripada fitur banyak tapi bolong.