- Part 1: Setup & Menampilkan Data
- Part 2: CRUD (Tambah, Edit, Hapus) (sedang dibaca)
- Part 3: Pencarian, Filter & Paginasi
- Part 4: Autentikasi Login
Pada Part 1, kita sudah membangun fondasi aplikasi katalog buku dengan CodeIgniter 4: instalasi, migration, seeder, model, controller, hingga menampilkan data dalam bentuk tabel. Namun aplikasi itu masih bersifat “baca saja” — kita belum bisa menambah, mengubah, atau menghapus buku dari antarmuka.
Di Part 2 ini kita akan melengkapinya menjadi aplikasi CRUD yang utuh: membuat form tambah buku, menyimpan data dengan validasi, mengedit dan memperbarui data, menghapus buku, serta menampilkan pesan notifikasi (flash message). Pastikan Anda sudah menyelesaikan Part 1 sebelum lanjut.

Apa Itu CRUD?
CRUD adalah singkatan dari empat operasi dasar terhadap data:
- Create — menambah data baru (form tambah buku).
- Read — menampilkan data (sudah kita buat di Part 1).
- Update — mengubah data yang ada (form edit).
- Delete — menghapus data.
Hampir semua aplikasi — mulai dari toko online, sistem inventaris, hingga blog — pada dasarnya adalah rangkaian operasi CRUD. Menguasainya berarti Anda menguasai inti pengembangan aplikasi web.
Menyiapkan Rute untuk CRUD
Pertama, lengkapi app/Config/Routes.php dengan rute untuk seluruh operasi. Kita pakai nama metode berbahasa Indonesia agar konsisten dengan Part 1:
$routes->get('buku', 'Buku::index');
$routes->get('buku/tambah', 'Buku::tambah');
$routes->post('buku/simpan', 'Buku::simpan');
$routes->get('buku/edit/(:num)', 'Buku::edit/$1');
$routes->post('buku/update/(:num)', 'Buku::update/$1');
$routes->get('buku/hapus/(:num)', 'Buku::hapus/$1');
Perhatikan pola umumnya: menampilkan form memakai metode GET, sedangkan menyimpan atau memperbarui data memakai POST. Placeholder (:num) menangkap ID buku dari URL.
Menambahkan Tombol Aksi di Daftar Buku
Agar pengguna bisa mengakses fungsi CRUD, tambahkan tombol pada halaman daftar. Kita ubah view app/Views/buku/index.php agar tidak lagi memakai helper tabel otomatis, melainkan menyusun tabel manual supaya bisa menyisipkan kolom Aksi:
<?= $this->extend('layouts/utama') ?>
<?= $this->section('konten') ?>
<h2>Daftar Buku</h2>
<a href="/buku/tambah" class="tombol">+ Tambah Buku</a>
<table border="1" cellpadding="8" cellspacing="0">
<thead>
<tr>
<th>ID</th><th>Judul</th><th>Penulis</th>
<th>Harga</th><th>Stok</th><th>Aksi</th>
</tr>
</thead>
<tbody>
<?php foreach ($daftar as $b): ?>
<tr>
<td><?= esc($b['id']) ?></td>
<td><?= esc($b['judul']) ?></td>
<td><?= esc($b['penulis']) ?></td>
<td>Rp <?= number_format($b['harga'], 0, ',', '.') ?></td>
<td><?= esc($b['stok']) ?></td>
<td>
<a href="/buku/edit/<?= $b['id'] ?>">Edit</a>
<a href="/buku/hapus/<?= $b['id'] ?>"
onclick="return confirm('Yakin hapus buku ini?')">Hapus</a>
</td>
</tr>
<?php endforeach ?>
</tbody>
</table>
<?= $this->endSection() ?>
Fungsi esc() penting untuk mencegah serangan XSS dengan meng-escape output, sedangkan number_format() merapikan tampilan harga menjadi format rupiah.
Membuat Form Tambah Buku
Selanjutnya buat metode tambah() di controller Buku yang menampilkan form kosong:
public function tambah()
{
return view('buku/form', [
'judul_halaman' => 'Tambah Buku',
'aksi' => '/buku/simpan',
'buku' => null,
]);
}
Lalu buat view form di app/Views/buku/form.php. Form ini akan kita pakai bersama untuk tambah maupun edit:
<?= $this->extend('layouts/utama') ?>
<?= $this->section('konten') ?>
<h2><?= esc($judul_halaman) ?></h2>
<?php if (session('errors')): ?>
<div class="kotak-error">
<ul>
<?php foreach (session('errors') as $pesan): ?>
<li><?= esc($pesan) ?></li>
<?php endforeach ?>
</ul>
</div>
<?php endif ?>
<form action="<?= $aksi ?>" method="post">
<?= csrf_field() ?>
<label>Judul Buku</label>
<input type="text" name="judul" value="<?= old('judul', $buku['judul'] ?? '') ?>">
<label>Penulis</label>
<input type="text" name="penulis" value="<?= old('penulis', $buku['penulis'] ?? '') ?>">
<label>Harga</label>
<input type="number" name="harga" value="<?= old('harga', $buku['harga'] ?? '') ?>">
<label>Stok</label>
<input type="number" name="stok" value="<?= old('stok', $buku['stok'] ?? '') ?>">
<label>Tanggal Terbit</label>
<input type="date" name="terbit" value="<?= old('terbit', $buku['terbit'] ?? '') ?>">
<button type="submit" class="tombol">Simpan</button>
</form>
<?= $this->endSection() ?>
Beberapa hal penting di form ini: csrf_field() menyisipkan token keamanan CSRF yang wajib ada pada form POST, sementara old('judul', ...) mengembalikan nilai input sebelumnya bila validasi gagal — atau nilai dari data buku saat mode edit.

Menyimpan Data Buku Baru
Sekarang buat metode simpan() yang memproses data form. Di sinilah aturan validasi yang kita definisikan di model (Part 1) bekerja otomatis:
public function simpan()
{
$model = new BukuModel();
$data = [
'judul' => $this->request->getPost('judul'),
'penulis' => $this->request->getPost('penulis'),
'harga' => $this->request->getPost('harga'),
'stok' => $this->request->getPost('stok'),
'terbit' => $this->request->getPost('terbit'),
];
if (! $model->save($data)) {
return redirect()->back()
->withInput()
->with('errors', $model->errors());
}
return redirect()->to('/buku')
->with('sukses', 'Buku baru berhasil ditambahkan.');
}
Metode save() pada model otomatis menjalankan validasi. Jika gagal, kita kembali ke form dengan withInput() (agar input lama tidak hilang) dan mengirim daftar error. Jika berhasil, kita alihkan (redirect) ke halaman daftar sambil menitipkan pesan sukses lewat flash message.
Menampilkan Pesan Error dan Input Lama
Berkat blok session('errors') di view form tadi, ketika validasi gagal pengguna akan melihat daftar kesalahan di bagian atas form — misalnya “Judul Buku harus diisi” atau “Harga hanya boleh berisi angka”. Nilai yang sudah diketik pun tetap tampil karena old(). Tambahkan sedikit CSS agar kotak error mencolok:
.kotak-error {
background-color: #fee2e2;
border: 1px solid #dc2626;
color: #991b1b;
padding: 12px 16px;
border-radius: 6px;
margin-bottom: 16px;
}

Menampilkan Pesan Sukses (Flash Message)
Flash message adalah pesan yang hanya muncul sekali setelah redirect, lalu hilang. Kita sudah mengirimnya lewat with('sukses', ...). Untuk menampilkannya, tambahkan blok berikut di bagian atas view daftar app/Views/buku/index.php, tepat sebelum judul:
<?php if (session('sukses')): ?>
<div class="kotak-sukses">
<?= esc(session('sukses')) ?>
</div>
<?php endif ?>
Dan CSS-nya:
.kotak-sukses {
background-color: #dcfce7;
border: 1px solid #16a34a;
color: #166534;
padding: 12px 16px;
border-radius: 6px;
margin-bottom: 16px;
}

Membuat Form Edit Buku
Untuk mengedit, kita ambil data buku berdasarkan ID lalu tampilkan di form yang sama. Buat metode edit():
public function edit($id)
{
$model = new BukuModel();
$buku = $model->find($id);
if (! $buku) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound('Buku tidak ditemukan');
}
return view('buku/form', [
'judul_halaman' => 'Edit Buku',
'aksi' => '/buku/update/' . $id,
'buku' => $buku,
]);
}
Karena view form kita rancang fleksibel (memakai $buku['judul'] ?? ''), form yang sama otomatis terisi data buku saat mode edit. Jika ID tidak ditemukan, kita lempar halaman 404 yang rapi.
Memperbarui Data Buku
Metode update() mirip dengan simpan(), hanya saja memakai update() pada model dengan menyertakan ID:
public function update($id)
{
$model = new BukuModel();
$data = [
'judul' => $this->request->getPost('judul'),
'penulis' => $this->request->getPost('penulis'),
'harga' => $this->request->getPost('harga'),
'stok' => $this->request->getPost('stok'),
'terbit' => $this->request->getPost('terbit'),
];
if (! $model->update($id, $data)) {
return redirect()->back()
->withInput()
->with('errors', $model->errors());
}
return redirect()->to('/buku')
->with('sukses', 'Data buku berhasil diperbarui.');
}
Menghapus Data Buku
Terakhir, operasi hapus. Berkat konfirmasi JavaScript (onclick="return confirm(...)") yang kita pasang di tombol Hapus, pengguna tidak akan menghapus data secara tak sengaja. Buat metode hapus():
public function hapus($id)
{
$model = new BukuModel();
$model->delete($id);
return redirect()->to('/buku')
->with('sukses', 'Buku berhasil dihapus.');
}
Metode delete() akan menghapus baris berdasarkan ID. Bila di model Anda mengaktifkan soft delete, data sebenarnya tidak dihapus permanen melainkan hanya ditandai — berguna bila Anda ingin fitur “pulihkan data”.
Kode Controller Buku Selengkapnya
Sebagai rangkuman, inilah controller app/Controllers/Buku.php secara utuh setelah semua metode CRUD ditambahkan:
<?php
namespace App\Controllers;
use App\Models\BukuModel;
class Buku extends BaseController
{
public function index()
{
$model = new BukuModel();
return view('buku/index', ['daftar' => $model->findAll()]);
}
public function tambah()
{
return view('buku/form', [
'judul_halaman' => 'Tambah Buku',
'aksi' => '/buku/simpan',
'buku' => null,
]);
}
public function simpan()
{
$model = new BukuModel();
if (! $model->save($this->request->getPost())) {
return redirect()->back()->withInput()->with('errors', $model->errors());
}
return redirect()->to('/buku')->with('sukses', 'Buku baru berhasil ditambahkan.');
}
public function edit($id)
{
$model = new BukuModel();
$buku = $model->find($id);
if (! $buku) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
}
return view('buku/form', [
'judul_halaman' => 'Edit Buku',
'aksi' => '/buku/update/' . $id,
'buku' => $buku,
]);
}
public function update($id)
{
$model = new BukuModel();
if (! $model->update($id, $this->request->getPost())) {
return redirect()->back()->withInput()->with('errors', $model->errors());
}
return redirect()->to('/buku')->with('sukses', 'Data buku berhasil diperbarui.');
}
public function hapus($id)
{
(new BukuModel())->delete($id);
return redirect()->to('/buku')->with('sukses', 'Buku berhasil dihapus.');
}
}
Perhatikan betapa ringkasnya kode ini — berkat model dengan aturan validasi bawaan, tiap metode cukup beberapa baris. Inilah kekuatan CodeIgniter 4 dalam mempercepat pengembangan CRUD.
Penutup
Kini aplikasi katalog buku Anda sudah lengkap dengan operasi tambah, tampil, edit, dan hapus, plus notifikasi sukses/gagal yang ramah pengguna. Anda telah mempraktikkan alur CRUD yang menjadi fondasi hampir semua aplikasi web.
Masih ada beberapa fitur penting yang belum kita bahas — pencarian dan filter data, paginasi untuk data yang banyak, serta autentikasi login agar hanya admin yang bisa mengelola buku. Semua itu akan kita kupas di Part 3. Sampai jumpa di lanjutannya!
Referensi: untuk pendalaman lebih lanjut, kunjungi dokumentasi resmi CodeIgniter 4.

