LaravelProyekFull-StackMahir6 mnt baca

Proyek Akhir: Blog Full-Stack Lengkap

Gabungkan semua: blog dengan auth, CRUD, komentar, dan upload.

Gambaran Proyek

Ini proyek akhir yang menggabungkan hampir semua modul Laravel: blog full-stack dengan halaman publik, dashboard penulis, komentar, upload cover, notifikasi, dan tags. Kerjakan bertahap; setiap fase bisa di-test sendiri sebelum lanjut.

Fase 1: Setup dan Auth

bash
composer create-project laravel/laravel blogku && cd blogku
composer require laravel/breeze --dev
php artisan breeze:install blade && php artisan migrate
npm install && npm run dev   # terminal terpisah

Breeze memberi register/login/logout + layout Blade yang rapi. Jangan bangun auth manual, fokus ke fitur blog.

Fase 2: Database

bash
php artisan make:model Post -m
php artisan make:model Comment -m
php artisan make:model Tag -m
php
// migration posts
Schema::create('posts', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained()->cascadeOnDelete();
    $table->string('title');
    $table->string('slug')->unique();
    $table->text('body');
    $table->string('cover')->nullable(); // path di disk public
    $table->timestamp('published_at')->nullable();
    $table->timestamps();
});

// migration comments
Schema::create('comments', function (Blueprint $table) {
    $table->id();
    $table->foreignId('post_id')->constrained()->cascadeOnDelete();
    $table->foreignId('user_id')->constrained()->cascadeOnDelete();
    $table->text('body');
    $table->timestamps();
});

// migration post_tag (pivot many-to-many)
Schema::create('post_tag', function (Blueprint $table) {
    $table->foreignId('post_id')->constrained()->cascadeOnDelete();
    $table->foreignId('tag_id')->constrained()->cascadeOnDelete();
    $table->primary(['post_id', 'tag_id']);
});

Relasi di model plus route binding via slug:

php
// app/Models/Post.php
public function user() { return $this->belongsTo(User::class); }
public function comments() { return $this->hasMany(Comment::class)->latest(); }
public function tags() { return $this->belongsToMany(Tag::class); }
public function getRouteKeyName(): string { return 'slug'; }

Buat factory + seeder agar punya data dummy: php artisan make:factory PostFactory, lalu php artisan db:seed.

Fase 3: Halaman Publik (Tanpa N+1)

php
// app/Http/Controllers/PostController.php
public function index()
{
    // with() = eager loading, paginate = 12 per halaman
    $posts = Post::with(['user', 'tags'])
        ->whereNotNull('published_at')
        ->latest('published_at')
        ->paginate(12);

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

public function show(Post $post)
{
    $post->load(['user', 'tags', 'comments.user']);

    return view('posts.show', compact('post'));
}

Satu with() menghindari N+1: tanpa ini, 12 post = 1 query post + 12 query user + 12 query tags. Cek dengan Laravel Debugbar sampai query per halaman masuk akal.

Fase 4: Dashboard CRUD + Upload Cover

bash
php artisan make:controller Dashboard/PostController
php artisan make:request StorePostRequest
php artisan make:policy PostPolicy --model=Post
php
// app/Http/Controllers/Dashboard/PostController.php
public function store(StorePostRequest $request)
{
    $data = $request->validated();
    $data['slug'] = Str::slug($data['title']).'-'.Str::random(6);

    if ($request->hasFile('cover')) {
        $data['cover'] = $request->file('cover')->store('covers', 'public');
    }

    $request->user()->posts()->create($data);

    return redirect()->route('dashboard.posts.index');
}

Validasi di StorePostRequest: title required, body min 50, cover nullable image max 2MB, tags array dengan tags.* exists. Setelah update, sinkronkan: $post->tags()->sync($request->tags). Saat ganti cover, hapus yang lama via Storage::disk('public')->delete($post->cover).

Fase 5: Komentar + Notifikasi

php
// app/Http/Controllers/CommentController.php
public function store(Request $request, Post $post)
{
    $data = $request->validate(['body' => 'required|string|max:1000']);

    $comment = $post->comments()->create([
        ...$data, 'user_id' => $request->user()->id,
    ]);

    // beri tahu pemilik post (jangan notifikasi diri sendiri)
    if ($post->user_id !== $request->user()->id) {
        $post->user->notify(new KomentarBaru($comment));
    }

    return back();
}

Buat notification KomentarBaru (channel mail + database, ShouldQueue) seperti di modul notification, plus link "Notifikasi" di navbar yang menampilkan $user->unreadNotifications.

Fase 6: Policy dan Keamanan

php
// app/Policies/PostPolicy.php
public function update(User $user, Post $post): bool
{
    return $user->id === $post->user_id;
}
public function delete(User $user, Post $post): bool
{
    return $user->id === $post->user_id;
}

Proteksi route dashboard: Route::middleware(['auth', 'verified'])->prefix('dashboard')->group(...). Tulis feature test: user B PUT ke post user A harus 403.

Fase 7: Checklist Selesai dan Deploy

  • Semua halaman jalan tanpa error, validasi rapi dengan pesan error
  • Tidak ada N+1 (verifikasi dengan Debugbar)
  • Policy ter-test: user lain tidak bisa edit/hapus
  • php artisan test hijau

Untuk deploy ke shared hosting/VPS: APP_ENV=production, APP_DEBUG=false, php artisan optimize, document root ke folder public, dan storage/ writable. Cantumkan URL demo di CV, employer lebih percaya demo hidup daripada screenshot.

Catatan teknis: Slug unik penting untuk SEO dan URL cantik. Tambahan Str::random(6) mencegah tabrakan saat dua post berjudul sama. Dan jangan lupa: kolom slug harus punya unique index di migration, kalau tidak validasi keunikan tidak terjamin di level database.

Tantangan

Wireframe Dulu

Gambar wireframe 5 halaman blog (beranda, detail, dashboard, form post, login) di kertas/Figma. Tentukan juga tabel database-nya. Baru mulai coding setelah desain jelas.

Tugas

Tugas: Blog Full-Stack

Bangun blog full-stack sesuai spesifikasi. Kirimkan ringkasan: daftar fitur yang selesai, URL demo (jika di-deploy), dan 3 file yang paling kamu banggakan (paste kodenya). AI tutor menilai arsitektur, keamanan, dan kelengkapan.

Kriteria penilaian:

  • Semua fitur spesifikasi berfungsi
  • Policy dan validasi benar, tidak ada celah obvious
  • Tidak ada N+1 query di halaman daftar
  • Kode rapi mengikuti konvensi Laravel

AI tutor akan memeriksa bug, kesalahan syntax, dan memberi saran perbaikan.

Kuis Bab

Uji pemahamanmu: Bab 10: Arsitektur dan Proyek Akhir

Jawab 5 soal berikut, lalu tekan "Periksa Jawaban".

1.Apa beda register() dan boot() di Service Provider?

2.Apa keuntungan bind interface di container?

3.Kapan memakai Event/Listener?

4.Untuk API autentikasi SPA/mobile, pilih apa?

5.Langkah pertama sebelum coding proyek akhir menurut materi?