Di batch terakhir ini, PahamITian akan mengaplikasikan semua ilmu Laravel yang sudah dipelajari untuk membangun mini blog sederhana. Integrasikan routing, controller, model, migration, Blade, autentikasi, dan upload file menjadi satu proyek utuh.
- 1 Pengenalan Laravel: Memulai Petualangan Web dengan PHP Framework (Batch 1)
- 2 Routing dan Blade: Mengatur Alur Aplikasi dan Tampilan Cantik di Laravel (Batch 2)
- 3 Belajar Laravel Batch 3: Controller dan Form, Mengatur Logika dan Interaksi Pengguna
- 4 Belajar Laravel Batch 4: Database, Migration, dan Model - Mengelola Data Aplikasi
- 5 Belajar Laravel Batch 5: CRUD Lengkap - Menguasai Operasi Data Aplikasi
- 7 Belajar Laravel Batch 7: Authentication Dasar - Mengamankan Aplikasi dengan Login dan Register
- 8 Belajar Laravel Batch 6: Relasi Database Dasar - Menghubungkan Data Aplikasi
- 8 Belajar Laravel Batch 8: Upload File dan Storage - Mengelola Media Aplikasi
- 9 Belajar Laravel Batch 9: SEO dan Publish Status - Membuat Artikel Lebih Optimal dan Teratur
- 10 Belajar Laravel Batch 10: Mini Project Akhir - Membangun Blog Sederhana
- 11 Belajar Laravel Batch 11: Membangun RESTful API dengan Laravel
Halo, PahamITian! Selamat datang di batch terakhir dari seri tutorial Laravel dasar kita! Jika kamu sudah sampai di sini, itu artinya kamu sudah melewati banyak tantangan dan menyerap banyak ilmu. Salut! Sekarang, saatnya kita merangkum semua yang sudah dipelajari dan mengaplikasikannya dalam sebuah proyek nyata: Mini Blog Sederhana.
Batch ini adalah puncak dari perjalananmu. Kita akan menyatukan semua kepingan puzzle yang sudah kita kumpulkan dari Batch 1 hingga Batch 9. Ini bukan hanya tentang membuat fitur, tapi tentang bagaimana semua komponen Laravel bekerja sama secara harmonis untuk membentuk sebuah aplikasi web yang fungsional.
Tujuan Batch 10: Membangun Mini Blog Laravel
Di batch ini, kita akan membangun sebuah aplikasi blog sederhana dengan fitur-fitur utama:
- Login Admin: Untuk mengelola artikel dan kategori.
- CRUD Artikel: Membuat, membaca, memperbarui, dan menghapus artikel.
- CRUD Kategori: Membuat, membaca, memperbarui, dan menghapus kategori.
- Upload Gambar: Untuk
featured imageartikel. - Status Publish: Artikel bisa berupa
draftataupublished. - Halaman Publik: Menampilkan daftar artikel dan detail artikel yang sudah
published. - Relasi Data: Artikel terhubung dengan user yang membuatnya dan kategori.
Recap Singkat: Perjalanan Kita Sejauh Ini
Sebelum melangkah lebih jauh, mari kita ingat kembali apa saja yang sudah kita pelajari:
- Batch 1: Pengenalan Laravel - Apa itu Laravel, struktur folder, alur request.
- Batch 2: Routing dan Blade - Mengatur URL dan membuat tampilan dinamis.
- Batch 3: Controller dan Form - Memisahkan logika aplikasi dan menangani input pengguna.
- Batch 4: Database, Migration, dan Model - Membuat tabel database dan berinteraksi dengan data.
- Batch 5: CRUD Lengkap - Menguasai operasi dasar data (Create, Read, Update, Delete).
- Batch 6: Relasi Database Dasar - Menghubungkan antar tabel (One-to-Many).
- Batch 7: Authentication Dasar - Sistem login dan register untuk mengamankan aplikasi.
- Batch 8: Upload File dan Storage - Mengelola file yang diunggah pengguna.
- Batch 9: SEO dan Publish Status - Mengoptimalkan artikel untuk mesin pencari dan mengatur status publikasi.
Sekarang, mari kita satukan semuanya!
Struktur Pengerjaan Langkah demi Langkah: Mini Blog Project
Kita akan melanjutkan dari proyek yang sudah kita bangun di batch-batch sebelumnya. Pastikan kamu sudah memiliki project Laravel yang berfungsi dengan baik.
1. Persiapan Database & Model
Kita akan membutuhkan dua tabel baru: categories dan posts. Tabel users sudah ada dari setup autentikasi.
a. Migrasi untuk categories:
php artisan make:migration create_categories_table
Edit file migrasi yang baru dibuat (database/migrations/..._create_categories_table.php):
// ...
public function up(): void
{
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->string('name')->unique();
$table->string('slug')->unique();
$table->timestamps();
});
}
// ...
b. Migrasi untuk posts:
php artisan make:migration create_posts_table
Edit file migrasi yang baru dibuat (database/migrations/..._create_posts_table.php):
// ...
public function up(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->onDelete('cascade'); // Relasi ke tabel users
$table->foreignId('category_id')->constrained()->onDelete('cascade'); // Relasi ke tabel categories
$table->string('title');
$table->string('slug')->unique();
$table->text('content');
$table->string('featured_image')->nullable();
$table->enum('status', ['draft', 'published'])->default('draft');
$table->timestamp('published_at')->nullable();
$table->timestamps();
});
}
// ...
Jalankan migrasi:
php artisan migrate
c. Model Category dan Post:
Buat model:
php artisan make:model Category
php artisan make:model Post
Edit app/Models/Category.php:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
use HasFactory;
protected $fillable = ['name', 'slug'];
public function posts()
{
return $this->hasMany(Post::class);
}
}
Edit app/Models/Post.php:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use HasFactory;
protected $fillable = [
'user_id',
'category_id',
'title',
'slug',
'content',
'featured_image',
'status',
'published_at'
];
protected $casts = [
'published_at' => 'datetime',
];
public function user()
{
return $this->belongsTo(User::class);
}
public function category()
{
return $this->belongsTo(Category::class);
}
// Otomatis generate slug saat menyimpan
protected static function boot()
{
parent::boot();
static::creating(function ($post) {
$post->slug = \Illuminate\Support\Str::slug($post->title);
if (static::where('slug', $post->slug)->exists()) {
$post->slug = $post->slug . '-' . time();
}
});
static::updating(function ($post) {
if ($post->isDirty('title')) {
$post->slug = \Illuminate\Support\Str::slug($post->title);
if (static::where('slug', $post->slug)->where('id', '!=', $post->id)->exists()) {
$post->slug = $post->slug . '-' . time();
}
}
});
}
}
2. Controller untuk Admin (Kategori & Artikel)
Buat controller untuk mengelola kategori dan artikel di area admin.
php artisan make:controller Admin/CategoryController --resource
php artisan make:controller Admin/PostController --resource
a. app/Http/Controllers/Admin/CategoryController.php:
Implementasikan metode index, create, store, edit, update, destroy untuk CRUD kategori. Jangan lupa validasi dan flash message.
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Category;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
class CategoryController extends Controller
{
public function index()
{
$categories = Category::latest()->paginate(10);
return view('admin.categories.index', compact('categories'));
}
public function create()
{
return view('admin.categories.create');
}
public function store(Request $request)
{
$request->validate([
'name' => 'required|string|max:255|unique:categories'
]);
Category::create([
'name' => $request->name,
'slug' => Str::slug($request->name)
]);
return redirect()->route('admin.categories.index')->with('success', 'Kategori berhasil ditambahkan!');
}
public function edit(Category $category)
{
return view('admin.categories.edit', compact('category'));
}
public function update(Request $request, Category $category)
{
$request->validate([
'name' => 'required|string|max:255|unique:categories,name,' . $category->id
]);
$category->update([
'name' => $request->name,
'slug' => Str::slug($request->name)
]);
return redirect()->route('admin.categories.index')->with('success', 'Kategori berhasil diperbarui!');
}
public function destroy(Category $category)
{
$category->delete();
return redirect()->route('admin.categories.index')->with('success', 'Kategori berhasil dihapus!');
}
}
b. app/Http/Controllers/Admin/PostController.php:
Implementasikan metode index, create, store, show, edit, update, destroy untuk CRUD artikel. Termasuk upload gambar, validasi, dan flash message.
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Category;
use App\Models\Post;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class PostController extends Controller
{
public function index()
{
$posts = Post::with('user', 'category')->latest()->paginate(10);
return view('admin.posts.index', compact('posts'));
}
public function create()
{
$categories = Category::all();
return view('admin.posts.create', compact('categories'));
}
public function store(Request $request)
{
$request->validate([
'title' => 'required|string|max:255',
'category_id' => 'required|exists:categories,id',
'content' => 'required',
'featured_image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048',
'status' => 'required|in:draft,published'
]);
$imagePath = null;
if ($request->hasFile('featured_image')) {
$imagePath = $request->file('featured_image')->store('public/posts');
}
Post::create([
'user_id' => auth()->id(),
'category_id' => $request->category_id,
'title' => $request->title,
'slug' => Str::slug($request->title),
'content' => $request->content,
'featured_image' => $imagePath ? Storage::url($imagePath) : null,
'status' => $request->status,
'published_at' => $request->status === 'published' ? now() : null
]);
return redirect()->route('admin.posts.index')->with('success', 'Artikel berhasil ditambahkan!');
}
public function show(Post $post)
{
return view('admin.posts.show', compact('post'));
}
public function edit(Post $post)
{
$categories = Category::all();
return view('admin.posts.edit', compact('post', 'categories'));
}
public function update(Request $request, Post $post)
{
$request->validate([
'title' => 'required|string|max:255',
'category_id' => 'required|exists:categories,id',
'content' => 'required',
'featured_image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048',
'status' => 'required|in:draft,published'
]);
$imagePath = $post->featured_image;
if ($request->hasFile('featured_image')) {
// Hapus gambar lama jika ada
if ($post->featured_image) {
Storage::delete(str_replace('/storage', 'public', $post->featured_image));
}
$imagePath = $request->file('featured_image')->store('public/posts');
$imagePath = Storage::url($imagePath);
}
$post->update([
'category_id' => $request->category_id,
'title' => $request->title,
'slug' => Str::slug($request->title),
'content' => $request->content,
'featured_image' => $imagePath,
'status' => $request->status,
'published_at' => $request->status === 'published' && !$post->published_at ? now() : $post->published_at
]);
return redirect()->route('admin.posts.index')->with('success', 'Artikel berhasil diperbarui!');
}
public function destroy(Post $post)
{
// Hapus gambar terkait jika ada
if ($post->featured_image) {
Storage::delete(str_replace('/storage', 'public', $post->featured_image));
}
$post->delete();
return redirect()->route('admin.posts.index')->with('success', 'Artikel berhasil dihapus!');
}
}
3. Controller untuk Halaman Publik
Buat controller untuk menampilkan daftar dan detail artikel yang sudah published.
php artisan make:controller BlogController
a. app/Http/Controllers/BlogController.php:
<?php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Http\Request;
class BlogController extends Controller
{
public function index()
{
$posts = Post::with('user', 'category')
->where('status', 'published')
->whereNotNull('published_at')
->latest('published_at')
->paginate(9);
return view('blog.index', compact('posts'));
}
public function show(string $slug)
{
$post = Post::with('user', 'category')
->where('slug', $slug)
->where('status', 'published')
->whereNotNull('published_at')
->firstOrFail();
return view('blog.show', compact('post'));
}
}
4. Routing
Tambahkan route untuk area admin dan halaman publik di routes/web.php.
// ... (route login, register, dll)
Route::middleware(['auth'])->prefix('admin')->name('admin.')->group(function () {
Route::get('/dashboard', function () {
return view('admin.dashboard');
})->name('dashboard');
Route::resource('categories', Admin\CategoryController::class);
Route::resource('posts', Admin\PostController::class);
});
// Public Blog Routes
Route::get('/blog', [App\Http\Controllers\BlogController::class, 'index'])->name('blog.index');
Route::get('/blog/{slug}', [App\Http\Controllers\BlogController::class, 'show'])->name('blog.show');
// ... (route default welcome)
5. Views (Blade)
Buat struktur folder resources/views/admin dan resources/views/blog.
a. Admin Layout:
Buat resources/views/layouts/admin.blade.php sebagai layout utama untuk area admin. Sertakan navigasi untuk Kategori dan Artikel.
b. Views Kategori:
resources/views/admin/categories/index.blade.php(daftar kategori)resources/views/admin/categories/create.blade.php(form tambah kategori)resources/views/admin/categories/edit.blade.php(form edit kategori)
c. Views Artikel:
resources/views/admin/posts/index.blade.php(daftar artikel)resources/views/admin/posts/create.blade.php(form tambah artikel, dengan dropdown kategori dan input file)resources/views/admin/posts/edit.blade.php(form edit artikel)resources/views/admin/posts/show.blade.php(detail artikel)
d. Views Blog Publik:
resources/views/blog/index.blade.php(daftar artikel publik, dengan pagination)resources/views/blog/show.blade.php(detail artikel publik)
Pastikan setiap form memiliki @csrf dan menampilkan error validasi (@error directive).
6. Konfigurasi Storage
Pastikan symbolic link untuk storage sudah dibuat agar gambar bisa diakses publik.
php artisan storage:link
Checklist Final Proyek Mini Blog
Setelah semua langkah di atas selesai, cek apakah fitur-fitur berikut sudah berfungsi dengan baik:
- [ ] Sistem Login dan Register berfungsi.
- [ ] Area admin (
/admin) hanya bisa diakses setelah login. - [ ] CRUD Kategori berfungsi penuh (Tambah, Lihat, Edit, Hapus).
- [ ] CRUD Artikel berfungsi penuh (Tambah, Lihat, Edit, Hapus).
- [ ] Upload gambar untuk artikel berfungsi dan gambar ditampilkan dengan benar.
- [ ] Status
draft/publisheduntuk artikel berfungsi. - [ ] Artikel yang
drafttidak muncul di halaman publik. - [ ] Halaman daftar artikel publik (
/blog) menampilkan artikel yangpublished. - [ ] Halaman detail artikel publik (
/blog/{slug}) menampilkan artikel berdasarkan slug. - [ ] Artikel terhubung dengan user pembuat dan kategori.
- [ ] Validasi input di semua form berfungsi.
- [ ]
Flash message(success/error) muncul setelah operasi. - [ ] Pagination berfungsi di daftar artikel admin dan publik.
Tantangan Lanjutan (Opsional)
Jika kamu ingin memperkaya proyek mini blog ini, berikut beberapa ide:
- Fungsi Pencarian: Tambahkan fitur pencarian artikel.
- Komentar Artikel: Izinkan pengguna (atau hanya yang login) untuk berkomentar.
- Tagging Artikel: Tambahkan sistem tag untuk artikel.
- Rich Text Editor: Integrasikan editor seperti TinyMCE atau CKEditor untuk
contentartikel. - Deployment: Coba deploy aplikasi ke hosting sungguhan (misalnya Heroku, DigitalOcean, Vercel).
- Authorization: Buat sistem role (misalnya Admin, Editor) untuk membatasi akses.
Ringkasan Batch 10
Batch terakhir ini adalah bukti nyata bahwa kamu sudah menguasai dasar-dasar Laravel. Dengan membangun mini blog, kamu tidak hanya sekadar mengikuti tutorial, tapi benar-benar mengintegrasikan semua konsep menjadi satu aplikasi utuh. Ini adalah fondasi yang sangat kuat untuk membangun aplikasi Laravel yang lebih kompleks di masa depan.
Latihan Praktik
Tugas Utama: Bangunlah proyek mini blog ini dari awal (atau lanjutkan dari proyek sebelumnya) dengan mengikuti semua langkah yang telah dijelaskan. Pastikan semua fitur yang ada di Checklist Final Proyek Mini Blog berfungsi dengan baik.
Pertanyaan Cek Pemahaman
- Apa pentingnya mengintegrasikan semua komponen Laravel (routing, controller, model, view, auth, storage) dalam satu proyek?
- Bagaimana cara memastikan hanya artikel yang berstatus
publishedyang muncul di halaman publik? - Jelaskan alur kerja ketika sebuah gambar diunggah untuk
featured imagesebuah artikel, mulai dari form hingga penyimpanan di database dan server. - Mengapa kita perlu membuat
sluguntuk artikel, dan bagaimana cara mengotomatisasinya di Laravel? - Apa saja tantangan yang kamu hadapi saat mengintegrasikan berbagai fitur ini, dan bagaimana kamu menyelesaikannya?
Kesalahan Umum Pemula
- Lupa
php artisan storage:link: Gambar tidak akan muncul di browser. - Salah konfigurasi relasi di Model:
user_idataucategory_idtidak terisi, atau error saat eager loading. - Validasi tidak lengkap: Mengizinkan data kosong atau tidak valid masuk ke database.
- Lupa
@csrfdi form: Error419 Page Expiredsaat submit form POST. - Kesalahan di Blade: Variabel tidak terdefinisi atau sintaks Blade yang salah.
- Middleware
authtidak diterapkan: Area admin bisa diakses tanpa login. published_attidak diatur: Artikelpublishedtidak muncul karenapublished_atmasihnull.
Persiapan untuk Batch Berikutnya
Selamat, PahamITian! Kamu telah menyelesaikan seluruh kurikulum dasar Laravel ini. Ini adalah pencapaian besar! Tidak ada batch berikutnya dalam seri dasar ini, namun perjalanan belajarmu tidak berhenti di sini. Teruslah bereksplorasi dengan Laravel, coba tantangan lanjutan yang sudah diberikan, atau mulai proyek pribadi yang lebih ambisius. Dunia pengembangan web sangat luas, dan Laravel adalah alat yang sangat powerful untuk menjelajahinya.
Tetap semangat belajar dan jangan pernah berhenti berkreasi! Sampai jumpa di tutorial Pahamit.com lainnya!