Belajar Laravel 13 #6: Controller, Request, Validation & CRUD
Heru Iryanto
Teacher, Content Creator and Full-Stack DeveloperDi materi Struktur Modern dan Routing, kita menjejalkan perintah untuk memunculkan teks (return "Halo") langsung di dalam file routes/web.php.
Cara ini sangat terlarang di industri! Rute (Satpam) tugasnya hanya membaca alamat, bukan mengeksekusi logika berat. Logika eksekusi (seperti menyimpan data form atau memeriksa password) harus diserahkan ke huruf ‘C’ dari MVC, yaitu Controller (Sang Pelayan). Kali ini kita akan banyak menggunakan Controller untuk pembuatan proses CRUD.
CRUD adalah singkatan dari Create, Read, Update, Delete. Ini adalah operasi dasar yang dapat dilakukan pada database. Saat ini, kita akan membuat “Artikel Online” sederhana yang memiliki fitur CRUD dan Show untuk mengelola data Artikel. Berikut adalah penjelasan detail mengenai setiap operasi:
1️⃣ Read (Menampilkan Data)
Sebelum kita membaca / menampilkan isi database, kita akan berkenalan dulu dengan Controller dan melakukan operasi Read menggunakan Controller.
Controller adalah bagian dari MVC yang bertugas untuk menangani logika bisnis aplikasi. Controller bertugas untuk menerima permintaan dari pengguna, memprosesnya, dan mengembalikan hasil ke pengguna. Controller juga bisa memanggil Model untuk mengambil data dari database atau melakukan operasi lainnya.
1.1. Membuat Controller
Gunakan terminal artisan untuk mempekerjakan Pelayan baru yang khusus mengurusi urusan Artikel:
php artisan make:controller ArtikelController
(File akan terbentuk di app/Http/Controllers/ArtikelController.php)
Mari buka filenya, dan buat aksi-aksi (Method) standar di dalamnya:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Artikel; // Jangan lupa panggil Model!
class ArtikelController extends Controller
{
// Aksi 1: Menampilkan Halaman Tabel Semua Berita
public function index() {
$semua_data = Artikel::all(); // Model Bekerja
return view('artikel.index', ['berita' => $semua_data]); // View Bekerja
}
}
Pada kode diatas, terdapat 2 baris penting :
$semua_data = Artikel::all();Artinya : “Ambil semua data dari Tabel Artikel menggunakan Model Artikel”. Seperti yang sudah kita pelajari di materi Eloquent ORM. Eloquent akan mempermudah kita dalam berinteraksi dengan database.return view('artikel.index', ['berita' => $semua_data]);Artinya : “Tampilkan halamanindexyang ada di dalam folderartikel, dan kirimkan data tersebut dengan nama variabel baruberita”
Jika menggunakan compact, maka penulisan function index nya menjadi:
public function index() {
$semua_data = Artikel::all();
return view('artikel.index', compact('semua_data'));
}
Untuk sementara, kita tidak memakai yang compact ini terlebih dahulu. Untuk project-project selanjutnya, kita baru menggunakan compact ini.
1.2. Membuat View dan Layout untuk Artikel
Di folder views, buat layout untuk artikel artikel_layout.blade.php yang dijadikan sebagai master layout, lalu beri kode seperti ini :
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>@yield('title', 'Artikel')</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-100 text-gray-800 min-h-screen">
<header class="bg-white shadow-sm">
<div class="max-w-4xl mx-auto px-4 py-6">
<h1 class="text-2xl md:text-3xl font-bold">@yield('title', 'Judul Artikel')</h1>
</div>
</header>
<main class="max-w-4xl mx-auto px-4 py-8">
<article class="bg-white rounded-xl shadow p-6 md:p-8 prose max-w-none">
@yield('content')
</article>
</main>
</body>
</html>
Inti dari kode diatas ada pada @yield.
@yield digunakan untuk menampilkan data yang dikirimkan dari view. Sintaksnya @yield('nama_variabel').
Ada parameter default yang ada di dalam @yield('title', 'Artikel'), artinya jika tidak ada data yang dikirimkan dari view, maka akan menampilkan “Artikel”.
Sedangkan pada @yield('content'), tidak memiliki parameter default, artinya jika tidak ada data yang dikirimkan dari view, maka tidak akan menampilkan apa-apa.
Setelah artikel_layout.blade.php selesai, buat folder artikel di dalam folder views. Lalu buat file index.blade.php di dalam folder artikel tersebut dengan kode sebagai berikut :
@extends('artikel_layout')
@section('title', 'Belajar Laravel untuk Pemula')
@section('content')
<h1 class="text-3xl font-bold mb-4">Daftar Artikel</h1>
<table class="min-w-full bg-white border border-gray-300 rounded-lg overflow-hidden">
<thead class="bg-gray-100">
<tr>
<th class="px-6 py-3 text-left text-sm font-medium text-gray-700">ID</th>
<th class="px-6 py-3 text-left text-sm font-medium text-gray-700">Judul</th>
<th class="px-6 py-3 text-left text-sm font-medium text-gray-700">Isi</th>
</tr>
</thead>
<tbody>
@foreach ($berita as $artikel)
<tr class="border-t border-gray-200">
<td class="px-6 py-4 whitespace-nowrap">{{ $artikel->id }}</td>
<td class="px-6 py-4 whitespace-nowrap">{{ $artikel->judul }}</td>
<td class="px-6 py-4 whitespace-nowrap">{{ \Illuminate\Support\Str::limit($artikel->konten, 10) }}</td>
</tr>
@endforeach
</tbody>
@endsection
Inti dari kode index.blade.php diatas ada pada @extends, @section dan @foreach.
@extends('artikel_layout') digunakan untuk memanggil file artikel_layout.blade.php yang dijadikan sebagai master layoutnya. Sintaksnya @extends('nama_view_master').
@section('title', 'Belajar Laravel untuk Pemula') digunakan untuk memberikan nilai default pada @yield('title') yang ada di file artikel_layout.blade.php. Sintaksnya @section('nama_view', 'data').
@section('content') digunakan untuk membungkus konten yang akan ditampilkan. Sintaksnya @section('nama_variabel'). Bagian ini harus ada di setiap file view yang ingin ditampilkan. @endsection digunakan untuk menutup @section yang berbentuk blok seperti ini.
@foreach ($berita as $artikel) digunakan untuk menampilkan data secara berulang dari database atau dari yang sudah ditentukan di controller.
1.3. Merangkai Kabel antara Rute ke Controller:
Sekarang, mari ubah satpam Anda di routes/web.php agar melemparkan tanggung jawabnya ke pelayan.
use App\Http\Controllers\ArtikelController; // Panggil Marga Pelayan
// Menampilkan semua artikel
Route::get('/artikel', [ArtikelController::class, 'index'])
->name('artikel.index');
Maksud dari kode diatas adalah, ketika user mengakses alamat http://localhost:8000/artikel, maka akan diarahkan ke method index di dalam ArtikelController.
name('artikel.index') digunakan untuk memberikan nama pada route tersebut, sehingga memudahkan untuk memanggil route tersebut di view.
1.4. Test di Browser
Jalankan php artisan serve, lalu buka browser Anda dan ketik alamat:
http://localhost:8000/artikel
Anda akan melihat halaman yang menampilkan judul “Daftar Artikel” dan seluruh isi tabel yang sudah dibuat menggunakan seeding sebelumnya.
2️⃣ Create (Menambahkan Data)
Untuk menambahkan data menggunakan Create, kita membutuhkan view untuk menampilkan form yang berisi judul dan konten, route untuk mengarahkan ke form dan menyimpan data, dan controller untuk menangani proses penyimpanan data.
2.1 Membuat View
Mari kita buat file create.blade.php di dalam folder artikel untuk menampilkan form inputan. Kodenya seperti ini:
@extends('artikel_layout')
@section('title', 'Belajar Laravel untuk Pemula')
@section('content')
<h1 class="text-3xl font-bold mb-4">Tambah Artikel Baru</h1>
<form action="{{ route('artikel.store') }}" method="POST" class="space-y-4">
@csrf
<div>
<label for="judul" class="block text-sm font-medium text-gray-700">Judul</label>
<input type="text" name="judul" id="judul" class="w-full border p-2 rounded-md mt-2">
</div>
<div>
<label for="konten" class="block text-sm font-medium text-gray-700">Konten</label>
<textarea name="konten" id="konten" rows="5" class="w-full border p-2 rounded-md mt-2"></textarea>
</div>
<button type="submit" class="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition">Simpan Artikel</button>
</form>
@endsection
Yang penting dari kode diatas adalah pada bagian name. Dimana name="judul" dan name="konten" harus sama persis dengan name yang ada di database/controller.
@extends dan @section sudah saya jelaskan di poin 1.2.
Setiap form di laravel wajib menyertakan @csrf. @csrf digunakan untuk mencegah serangan CSRF. @csrf adalah kependekan dari Cross-Site Request Forgery. CSRF adalah sejenis serangan di mana penyerang mencoba membuat pengguna yang sudah login untuk melakukan tindakan yang tidak mereka inginkan.
2.2. Menambahkan Route
Tambahkan route untuk menambah artikel baru serta route untuk menyimpannya di web.php:
// Form tambah artikel
Route::get('/artikel/create', [ArtikelController::class, 'create'])
->name('artikel.create');
// Simpan artikel baru
Route::post('/artikel', [ArtikelController::class, 'store'])
->name('artikel.store');
Rute untuk menyimpan / menangkap form tersebut di web.php BUKAN menggunakan Route::get, melainkan Route::post!
2.3 Menambahkan function di Controller :
Sekarang tambahkan function create dan function store di ArtikelController.php :
// Aksi 2: Memunculkan Tampilan Form Tambah (Kosongan)
public function create() {
return view('artikel.create');
}
// Aksi 3: Menyimpan Data Baru ke Database
public function store(Request $request) {
// Validasi Data
$request->validate([
'judul' => 'required',
'konten' => 'required',
]);
// Simpan data ke database
// Cara 1 : Membangun Objek Baru lalu Mendorongnya (Save)
$baru = new Artikel();
$baru->judul = $request->judul; // Ambil dari Kotak "judul"
$baru->konten = $request->konten; // Ambil dari Kotak "konten"
$baru->save(); // DORONG ke Database!
// Redirect ke halaman daftar artikel
return redirect()->route('artikel.index');
}
Kita menggunakan new Artikel() untuk membuat objek baru yang akan disimpan ke database, seperti yang sudah kita pelajari di materi sebelumnya yaitu materi Eloquent ORM. Pastikan name di <input ...> harus sama dengan yang di $request di new Artikel().
Silakan test di browser dengan mengakases :
http://localhost:8000/artikel/create
Isi inputan judul dan konten nya, lalu tekan tombol “Simpan Artikel”. Setelah berhasil tersimpan, dia akan mengarahkan ke halaman list Daftar Artikel. Cek apakah data yang baru kamu masukkan ada disana, atau bisa juga mengecek langsung ke database nya di phpMyAdmin atau TablePlus nya.
Di PHP kuno, jika pengunjung menekan tombol Simpan di Form HTML, Anda menangkap lemparan isian teksnya menggunakan variabel gaib $_POST['judul'].
Hal ini dihapus total di Framework PHP Modern! Anda harus menggunakan fitur Dependency Injection bernama Request $request.
Apa yang terjadi jika pengunjung SENGAJA TIDAK MENGISI APA-APA (KOSONG) lalu langsung memencet tombol Simpan?
Sistem akan Error!
Kita butuh Validasi. Di PHP murni, Anda akan menggunakan if (empty($_POST['judul'])) { return error... }. Sangat bertele-tele.
Di Laravel, gunakan filter validate(). Ini sangat menakjubkan.
Selain menggunakan new Artikel()... untuk menyimpan data, kamu juga bisa menggunakan sintaks sederhana seperti ini:
// Cara 2: Mass Assignment
Artikel::create($request->all());
// Cara 3: Array Association
Artikel::create([
'judul' => $request->judul,
'konten' => $request->konten,
]);
3️⃣ Show (Menampilkan Detail Data)
Sama seperti proses read dan create yang sudah kita lakukan, kita membutuhkan controller, route dan view untuk menampilkan detail artikel. Kita masih menggunakan id sebagai penunjuk data yang akan ditampilkan. Next project kita akan menggunakan Route Model Binding untuk memanggil data, lebih canggih dan singkat.
3.1 Mengedit Controller
Sekarang mari kita buat fungsi untuk menampilkan detail artikel. Tambahkan function show() ke dalam ArtikelController.php:
// Aksi 4: Menampilkan Detail Satu Data
public function show($id) {
// Cari data berdasarkan ID
$artikel = Artikel::find($id);
// Tampilkan di view detail.blade.php
return view('artikel.detail', compact('artikel'));
}
Kita menggunakan Artikel::find($id) untuk mencari data berdasarkan ID seperti yang sudah kita pelajari pada materi Eloquent ORM. Next project kita akan menggunakan Route Model Binding untuk memanggil data, lebih canggih dan singkat.
3.2 Membuat View
Buat file detail.blade.php di dalam folder artikel untuk menampilkan detail artikel :
@extends('artikel_layout')
@section('title', 'Detail Artikel')
@section('content')
<div class="mb-6">
<h1 class="text-3xl font-bold mb-2">{{ $artikel->judul }}</h1>
<p class="text-gray-600">ID: {{ $artikel->id }}</p>
<p class="text-gray-600">Dibuat: {{ $artikel->created_at->format('d F Y H:i') }}</p>
</div>
<div class="prose max-w-none">
{!! $artikel->konten !!}
</div>
@endsection
3.3 Menambahkan Route
Tambahkan route untuk menampilkan detail artikel di web.php :
// Tampilkan detail satu artikel
Route::get('/artikel/{id}', [ArtikelController::class, 'show'])
->name('artikel.show');
3.4 Mengedit index.blade.php
Edit file index.blade.php untuk menampilkan link detail artikel, link tambah, edit dan tombol delete, serta merubah tampilannya dari list biasa menjadi card-card yang lebih rapi:
@extends('artikel_layout')
@section('title', 'Belajar Laravel untuk Pemula')
@section('content')
<h1 class="text-3xl font-bold mb-6">Daftar Artikel</h1>
<a href="{{ route('artikel.create') }}" class="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition mb-4 inline-block">
Tambah Artikel
</a>
<div class="space-y-4">
@foreach ($berita as $artikel)
<div class="bg-white p-4 rounded-md shadow-sm border">
<h3 class="text-xl font-bold mb-2">{{ $artikel->judul }}</h3>
<div class="prose mb-4">
{{ $artikel->konten }}
</div>
<div class="flex space-x-2">
<a href="{{ route('artikel.show', $artikel->id) }}" class="bg-green-600 text-white px-3 py-1 rounded-md hover:bg-green-700 transition">
Baca Detail
</a>
</div>
</div>
@endforeach
</div>
@endsection
Inti pada kodingan diatas sebenarnya sama seperti sebelumnya, hanya saja kita menambahkan :
Tombol Baca Detail, link ke halaman detail artikel:
<a href="{{ route('artikel.show', $artikel->id) }}" class="bg-green-600 text-white px-3 py-1 rounded-md hover:bg-green-700 transition"> Baca Detail </a>Tombol Tambah, link ke halaman create:
<a href="{{ route('artikel.create') }}" class="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition mb-4 inline-block"> Tambah Artikel </a>Tampilan yang lebih rapi dengan card-card.
Sekarang coba klik tombol “Baca Detail” nya, apakah datanya sudah muncul ? Jika sudah, berarti sudah berhasil!
Tombol “Tambah” juga harusya sudah berfungsi dengan benar dan muncul form tambah artikel nya.
4️⃣ Update (Merubah Data & Menyimpan Perubahan)
Sama seperti pada materi sebelumnya, pada materi kali ini kita juga akan menambahkan beberapa fungsi pada controller, route dan view.
4.1 Mengedit Controller
Tambahkan function edit() dan function update() ke dalam ArtikelController.php:
// Aksi 5: Menampilkan Form Edit
public function edit($id) {
$artikel = Artikel::find($id);
return view('artikel.edit', compact('artikel'));
}
// Aksi 6: Menyimpan Perubahan Data
public function update(Request $request, $id) {
// Validasi Data
$request->validate([
'judul' => 'required',
'konten' => 'required',
]);
// Cari data berdasarkan ID
$artikel = Artikel::find($id);
$artikel->judul = $request->judul;
$artikel->konten = $request->konten;
$artikel->save();
return redirect('/artikel')->with('success', 'Artikel berhasil diupdate!');
}
Sama seperti fungsi store() kemarin, pada fungsi edit() dan update() juga menggunakan Artikel::find($id) untuk mencari data berdasarkan ID. Next Project kita akan menggunakan Mass Assignment untuk update data, lebih canggih dan singkat.
Pada kode diatas, kita juga menggunakan fungsi with() untuk membawa pesan success ke halaman artikel. Agar pesan tersebut muncul, kita harus menambahkan pesan success di halaman artikel. Yaitu dengan menambahkan kode berikut di index.blade.php, misalnya ditempatkan setelah kode @section('content'):
@if(session('success'))
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded relative mb-4" role="alert">
<strong class="font-bold">Success!</strong>
<span class="block sm:inline">{{ session('success') }}</span>
</div>
@endif
Jika nanti berhasil maka akan muncul pesan success berwarna hijau setelah kamu mengedit artikel dan menekan tombol “Update Artikel”. Jika kamu refresh halaman tersebut, maka pesan success akan hilang. Itu karena pesan success hanya akan muncul sekali.
Hal yang sama juga bisa kamu lakukan pada function store() di ArtikelController.php dengan menambahkan with() pada bagian return view() nya.
Oh iya, sebagai tambahan, untuk update, kamu juga bisa menggunakan kode seperti ini:
$artikel->update([
'judul' => $request->judul,
'konten' => $request->konten,
]);
Untuk validasi update juga bisa diterapkan bisa juga tidak, tergantung kebutuhan.
4.2 Membuat View
Buat file edit.blade.php di dalam folder artikel untuk menampilkan form edit artikel:
@extends('artikel_layout')
@section('title', 'Edit Artikel')
@section('content')
<h1 class="text-3xl font-bold mb-6">Edit Artikel</h1>
<form action="{{ route('artikel.update', $artikel->id) }}" method="POST">
@csrf
@method('PUT')
<div class="mb-4">
<label class="block text-gray-700">Judul</label>
<input type="text" name="judul" value="{{ $artikel->judul }}"
class="w-full border p-2 rounded-md mt-2">
</div>
<div class="mb-4">
<label class="block text-gray-700">Konten</label>
<textarea name="konten" rows="8" class="w-full border p-2 rounded-md mt-2">{{ $artikel->konten }}</textarea>
</div>
<button type="submit" class="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition">
Update Artikel
</button>
<a href="{{ route('artikel.index') }}" class="bg-gray-600 text-white px-4 py-2 rounded-md hover:bg-gray-700 transition">
Kembali
</a>
</form>
@endsection
4.3 Menambahkan Route
Tambahkan route untuk menampilkan edit dan update artikel di web.php:
// Tampilkan form edit satu artikel
Route::get('/artikel/{id}/edit', [ArtikelController::class, 'edit'])
->name('artikel.edit');
// Proses update artikel
Route::put('/artikel/{id}', [ArtikelController::class, 'update'])
->name('artikel.update');
4.4 Menambahkan tombol edit
Silakan tambahkan tombol edit di file index.blade.php setelah tombol “Baca Detail”. Kode tombol editnya seperti ini :
<a href="{{ route('artikel.edit', $artikel->id) }}" class="bg-yellow-600 text-white px-3 py-1 rounded-md hover:bg-yellow-700 transition">
Edit
</a>
Karena tombol “Edit” nya sudah dibuat, maka sekarang coba kamu edit salah satu artikel. Apakah berhasil? Jika berhasil maka akan muncul pesan success berwarna hijau setelah kamu mengedit artikel dan menekan tombol “Update Artikel”. Jika kamu refresh halaman tersebut, maka pesan success akan hilang. Itu karena pesan success hanya akan muncul sekali.
5️⃣ Delete (Menghapus Data)
Untuk menghapus data, kita tidak membutuhkan view, yang kita butuhkan hanyalah bagian controller dan route saja. Tidak perlu membuat file view lagi.
5.1 Menambahkan Controller
Silahkan tambahkan function destroy() dan function delete() ke dalam ArtikelController.php:
// Aksi 7: Menghapus Data
public function destroy($id) {
$artikel = Artikel::find($id);
$artikel->delete();
return redirect('/artikel')->with('success', 'Artikel berhasil dihapus!');
}
5.2 Menambahkan Route
Kemudian tambahkan route untuk menghapus data di web.php:
// Proses delete artikel
Route::delete('/artikel/{id}', [ArtikelController::class, 'destroy'])
->name('artikel.destroy');
5.3 Menambahkan Tombol Hapus
Silakan tambahkan tombol “Hapus” di file index.blade.php setelah tombol “Edit”. Kode tombol hapusnya seperti ini :
<form action="{{ route('artikel.destroy', $artikel->id) }}" method="POST">
@csrf
@method('DELETE')
<button type="submit" class="bg-red-600 text-white px-3 py-1 rounded-md hover:bg-red-700 transition">
Hapus
</button>
</form>
Karena tombol “Hapus” nya sudah dibuat, maka sekarang coba kamu delete salah satu artikel. Apakah berhasil? Jika berhasil maka akan muncul pesan success berwarna hijau setelah kamu menekan tombol “Hapus”. Jika kamu refresh halaman tersebut, maka pesan success akan hilang. Itu karena pesan success hanya akan muncul sekali.
Kita sudah belajar proses CRUD dengan validasi data dan notifikasi success. Keren bukan?
Untuk keseluruhan route untuk CRUD artikel adalah sebagai berikut:
//CRUD Artikel
Route::get('/artikel', [ArtikelController::class, 'index'])->name('artikel.index');
Route::get('/artikel/create', [ArtikelController::class, 'create'])->name('artikel.create');
Route::post('/artikel', [ArtikelController::class, 'store'])->name('artikel.store');
Route::get('/artikel/{id}', [ArtikelController::class, 'show'])->name('artikel.show');
Route::get('/artikel/{id}/edit', [ArtikelController::class, 'edit'])->name('artikel.edit');
Route::put('/artikel/{id}', [ArtikelController::class, 'update'])->name('artikel.update');
Route::delete('/artikel/{id}', [ArtikelController::class, 'destroy'])->name('artikel.destroy');
🗽 Kesimpulan
Jika Anda telah menguasai tiga pilar ini: Mendaftarkan Rute (Route), Mengeksekusi Logika Pelayan (Controller), dan Menampilkan Tampilan (View), Anda secara teori sudah menguasai 80% alur kerja Back-End Laravel sehari-hari.
Bahkan sistem raksasa pun fondasi intinya hanyalah Rute, Controller, dan View.
Sekarang arsitektur MVC kita telah sempurna! Namun, Anda belum memiliki sistem keanggotaan pengguna (User System). Membangun sistem Login, Register, dan Lupa Sandi sendiri akan menghabiskan waktu seminggu. Bagaimana jika Laravel bisa melakukannya untuk Anda dalam kurang dari 10 detik dan sudah digabungkan secara gaib dengan kekuatan desain Tailwind CSS materi sebelumnya? Temukan senjata nuklirnya di materi Autentikasi Instan (Laravel Breeze).
