CodeIgniter4: CRUD Data untuk Manajemen Postingan Blog
Published:
Implementasi CRUD (Create, Read, Update, Delete) merupakan bagian penting dalam pengembangan aplikasi web karena memungkinkan interaksi yang dinamis dengan data di database. Pada artikel ini kita mempraktikkan pengelolaan data postingan blog melalui panel admin, dimulai dari menampilkan daftar, menambah, mengubah, hingga menghapus data.
Praktikum ini melanjutkan project MyBlog dari artikel sebelumnya. Kita akan menerapkan CRUD data untuk mengelola tabel posts melalui panel admin. Pembahasan meliputi:
- Membuat admin post (route group dan controller)
- Menampilkan data (Read)
- Menambah data (Create)
- Mengubah data (Update)
- Menghapus data (Delete)
1. Membuat Admin Post
Pertama, buat route group untuk admin. Buka file app/Config/Routes.php dan tambahkan kode berikut:
$routes->group('admin', function($routes){
$routes->get('post', 'PostAdmin::index');
$routes->get('post/(:segment)/preview', 'PostAdmin::preview/$1');
$routes->add('post/new', 'PostAdmin::create');
$routes->add('post/(:segment)/edit', 'PostAdmin::edit/$1');
$routes->get('post/(:segment)/delete', 'PostAdmin::delete/$1');
});
Kode di atas membuat group route untuk bagian admin, artinya setiap route di dalam grup harus diawali dengan /admin. Misalnya, untuk membuka form create, alamatnya adalah /admin/post/new.
Ada dua metode yang digunakan pada route:
get()— hanya dapat diakses dengan metode HTTP GET.add()— dapat diakses menggunakan GET maupun POST. Digunakan pada routecreatedaneditkarena selain menampilkan form (GET), kita juga mengirim data (POST).
Membuat Controller PostAdmin
Buka terminal dan jalankan:
php spark make:controller PostAdmin
Output jika berhasil:
File created: APPPATH/Controllers/PostAdmin.php
Buka file app/Controllers/PostAdmin.php dan isi dengan kode lengkap berikut:
<?php
namespace App\Controllers;
use App\Controllers\BaseController;
use CodeIgniter\HTTP\ResponseInterface;
use App\Models\PostModel;
use CodeIgniter\Exceptions\PageNotFoundException;
class PostAdmin extends BaseController
{
public function index()
{
$post = new PostModel();
$data['posts'] = $post->findAll();
echo view('admin/admin_post_list', $data);
}
//--------------------------------------------------------------
public function preview($id)
{
$post = new PostModel();
$data['post'] = $post->where('id', $id)->first();
if(!$data['post']){
throw PageNotFoundException::forPageNotFound();
}
echo view('post_detail', $data);
}
//--------------------------------------------------------------
public function create()
{
// lakukan validasi
$validation = \Config\Services::validation();
$validation->setRules(['title' => 'required']);
$isDataValid = $validation->withRequest($this->request)->run();
// jika data valid, simpan ke database
if($isDataValid){
$post = new PostModel();
$post->insert([
"title" => $this->request->getPost('title'),
"content" => $this->request->getPost('content'),
"status" => $this->request->getPost('status'),
"slug" => url_title($this->request->getPost('title'), '-', TRUE)
]);
return redirect('admin/post');
}
// tampilkan form create
echo view('admin/admin_post_create');
}
//--------------------------------------------------------------
public function edit($id)
{
// ambil artikel yang akan diedit
$post = new PostModel();
$data['post'] = $post->where('id', $id)->first();
// lakukan validasi data artikel
$validation = \Config\Services::validation();
$validation->setRules([
'id' => 'required',
'title' => 'required'
]);
$isDataValid = $validation->withRequest($this->request)->run();
// jika data valid, simpan ke database
if($isDataValid){
$post->update($id, [
"title" => $this->request->getPost('title'),
"content" => $this->request->getPost('content'),
"status" => $this->request->getPost('status')
]);
return redirect('admin/post');
}
// tampilkan form edit
echo view('admin/admin_post_update', $data);
}
//--------------------------------------------------------------
public function delete($id)
{
$post = new PostModel();
$post->delete($id);
return redirect('admin/post');
}
}
Controller PostAdmin memiliki lima method:
index()— menampilkan daftar artikel.preview($id)— menampilkan pratinjau artikel berdasarkan$id.create()— membuat artikel baru.edit($id)— mengedit artikel berdasarkan$id.delete($id)— menghapus artikel berdasarkan$id.
2. Menampilkan Data (Read)
Pada controller PostAdmin, fitur read terdapat pada method index() dan preview().
Method index() mengambil semua data menggunakan findAll() dan mengirimnya ke view admin_post_list:
public function index()
{
$post = new PostModel();
$data['posts'] = $post->findAll();
echo view('admin/admin_post_list', $data);
}
Method preview($id) mengambil satu artikel berdasarkan $id menggunakan where() dan first(), lalu menampilkannya di view post_detail:
public function preview($id)
{
$post = new PostModel();
$data['post'] = $post->where('id', $id)->first();
if(!$data['post']){
throw PageNotFoundException::forPageNotFound();
}
echo view('post_detail', $data);
}
View Admin Post List
Buat folder baru admin di dalam app/Views, kemudian buat file admin_post_list.php:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MyBlog</title>
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="<?= base_url('css/bootstrap.min.css') ?>" />
</head>
<body>
<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
<div class="container">
<a class="navbar-brand" href="<?= base_url() ?>">MyBlog</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#navbarNav" aria-controls="navbarNav"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse justify-content-between" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="<?= base_url('admin/post') ?>">Blog</a>
</li>
</ul>
<ul class="navbar-nav">
<li class="nav-item">
<a href="<?= base_url('admin/post/new') ?>"
class="btn btn-primary mr-3">New Post</a>
</li>
<li class="nav-item">
<a class="nav-link" href="<?= base_url('admin/setting') ?>">Setting</a>
</li>
<li class="nav-item">
<a class="nav-link" href="<?= base_url('auth/logout') ?>">Logout</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="p-5 mb-4 bg-light rounded-3">
<div class="container py-5">
<h1 class="display-5 fw-bold">Blog > Admin</h1>
</div>
</div>
<div class="container">
<table class="table">
<thead>
<tr>
<th>#</th>
<th>Title</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<?php foreach($posts as $post): ?>
<tr>
<td><?= $post['id'] ?></td>
<td>
<strong><?= $post['title'] ?></strong><br>
<small class="text-muted"><?= $post['created_at'] ?></small>
</td>
<td>
<?php if($post['status'] === 'published'): ?>
<small class="text-success"><?= $post['status'] ?></small>
<?php else: ?>
<small class="text-muted"><?= $post['status'] ?></small>
<?php endif ?>
</td>
<td>
<a href="<?= base_url('admin/post/'.$post['id'].'/preview') ?>"
class="btn btn-sm btn-outline-secondary" target="_blank">Preview</a>
<a href="<?= base_url('admin/post/'.$post['id'].'/edit') ?>"
class="btn btn-sm btn-outline-secondary">Edit</a>
<a href="#"
data-href="<?= base_url('admin/post/'.$post['id'].'/delete') ?>"
onclick="confirmToDelete(this)"
class="btn btn-sm btn-outline-danger">Delete</a>
</td>
</tr>
<?php endforeach ?>
</tbody>
</table>
<!-- Modal Konfirmasi Delete -->
<div id="confirm-dialog" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-body">
<h2 class="h2">Are you sure?</h2>
<p>The data will be deleted and lost forever</p>
</div>
<div class="modal-footer">
<a href="#" role="button" id="delete-button"
class="btn btn-danger">Delete</a>
<button type="button" class="btn btn-secondary"
data-bs-dismiss="modal">Cancel</button>
</div>
</div>
</div>
</div>
<script>
function confirmToDelete(el) {
document.getElementById("delete-button")
.setAttribute("href", el.dataset.href);
var myModal = new bootstrap.Modal(
document.getElementById('confirm-dialog'), {
keyboard: false
});
myModal.show();
}
</script>
</div>
<div class="container py-4">
<footer class="pt-3 mt-4 text-muted border-top">
<div class="container">
© <?= Date('Y') ?>
</div>
</footer>
</div>
<!-- jQuery dan Bootstrap JS -->
<script src="<?= base_url('js/jquery.min.js') ?>"></script>
<script src="<?= base_url('js/bootstrap.bundle.min.js') ?>"></script>
</body>
</html>
Tampilan admin post list menampilkan daftar postingan dalam tabel dilengkapi tombol aksi: Preview, Edit, dan Delete. Tampilan preview sama seperti view post_detail pada sisi user, hanya saja URL-nya berbeda yaitu admin/post/{id}/preview.

3. Menambah Data (Create)
Method create() menangani dua kondisi: menampilkan form (GET) dan menyimpan data (POST).
Proses validasi:
$validation = \Config\Services::validation();
$validation->setRules(['title' => 'required']);
$isDataValid = $validation->withRequest($this->request)->run();
- Load library validasi data.
- Tetapkan aturan validasi: field
titleharus diisi (required). - Jalankan validasi terhadap data dari request.
Jika data valid, simpan ke database:
if($isDataValid){
$post = new PostModel();
$post->insert([
"title" => $this->request->getPost('title'),
"content" => $this->request->getPost('content'),
"status" => $this->request->getPost('status'),
"slug" => url_title($this->request->getPost('title'), '-', TRUE)
]);
return redirect('admin/post');
}
- Data diambil dari input form menggunakan
$this->request->getPost(). slugdihasilkan otomatis dari judul menggunakan helperurl_title().- Setelah data tersimpan, pengguna dialihkan ke halaman daftar postingan.
Jika data belum valid atau pertama kali membuka halaman, tampilkan form:
echo view('admin/admin_post_create');
View Form Create
Buat file app/Views/admin/admin_post_create.php:
<div class="container">
<form action="" method="post" id="text-editor">
<div class="form-group mb-2">
<label for="title">Title</label>
<input type="text" name="title" class="form-control"
placeholder="Post title" required>
</div>
<div class="form-group mb-2">
<textarea name="content" class="form-control" cols="30" rows="10"
placeholder="Write a great post!"></textarea>
</div>
<div class="form-group">
<button type="submit" name="status" value="published"
class="btn btn-primary">Publish</button>
<button type="submit" name="status" value="draft"
class="btn btn-secondary">Save to Draft</button>
</div>
</form>
</div>
Form ini memiliki dua tombol submit:
- Publish — menyimpan artikel dengan status
published. - Save to Draft — menyimpan artikel dengan status
draft.
4. Mengubah Data (Update)
Method edit($id) menangani pengambilan data lama, validasi, dan penyimpanan data yang diperbarui.
Ambil data artikel yang akan diedit:
$post = new PostModel();
$data['post'] = $post->where('id', $id)->first();
Lakukan validasi (field id dan title wajib diisi):
$validation = \Config\Services::validation();
$validation->setRules([
'id' => 'required',
'title' => 'required'
]);
$isDataValid = $validation->withRequest($this->request)->run();
Jika valid, update data di database:
if($isDataValid){
$post->update($id, [
"title" => $this->request->getPost('title'),
"content" => $this->request->getPost('content'),
"status" => $this->request->getPost('status')
]);
return redirect('admin/post');
}
// tampilkan form update
echo view('admin/admin_post_update', $data);
View Form Update
Buat file app/Views/admin/admin_post_update.php:
<div class="container">
<form action="" method="post" id="text-editor">
<input type="hidden" name="id" value="<?= $post['id'] ?>" />
<div class="form-group mb-2">
<label for="title">Title</label>
<input type="text" name="title" class="form-control"
placeholder="Post title" value="<?= $post['title'] ?>" required>
</div>
<div class="form-group mb-2">
<textarea name="content" class="form-control" cols="30" rows="10"
placeholder="Write a great post!"><?= $post['content'] ?></textarea>
</div>
<div class="form-group mb-2">
<button type="submit" name="status" value="published"
class="btn btn-primary">Publish</button>
<button type="submit" name="status" value="draft"
class="btn btn-secondary">Save to Draft</button>
</div>
</form>
</div>
Perbedaan utama dengan form create:
- Terdapat input hidden
iduntuk mengidentifikasi artikel yang diedit. - Field
titledantextareasudah terisi data lama dari database (value<?= $post['title'] ?>dan<?= $post['content'] ?>).
5. Menghapus Data (Delete)
Method delete($id) menerima parameter $id, menghapus data dari tabel posts, lalu mengalihkan ke halaman daftar:
public function delete($id)
{
$post = new PostModel();
$post->delete($id);
return redirect('admin/post');
}
Pada view admin_post_list.php, tombol delete tidak langsung menghapus data. Tombol ini memicu konfirmasi melalui modal Bootstrap:
<a href="#"
data-href="<?= base_url('admin/post/'.$post['id'].'/delete') ?>"
onclick="confirmToDelete(this)"
class="btn btn-sm btn-outline-danger">Delete</a>
Modal konfirmasi:
<div id="confirm-dialog" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-body">
<h2 class="h2">Are you sure?</h2>
<p>The data will be deleted and lost forever</p>
</div>
<div class="modal-footer">
<a href="#" role="button" id="delete-button"
class="btn btn-danger">Delete</a>
<button type="button" class="btn btn-secondary"
data-bs-dismiss="modal">Cancel</button>
</div>
</div>
</div>
</div>
Fungsi JavaScript confirmToDelete(el):
function confirmToDelete(el) {
document.getElementById("delete-button")
.setAttribute("href", el.dataset.href);
var myModal = new bootstrap.Modal(
document.getElementById('confirm-dialog'), {
keyboard: false
});
myModal.show();
}
- Mengambil elemen
delete-buttondan mengatur atributhrefdengan nilaidata-hrefdari tombol yang diklik. - Membuat instance Bootstrap modal untuk
confirm-dialogdan menonaktifkan penutupan modal via keyboard. - Menampilkan modal menggunakan method
show()agar pengguna dapat mengkonfirmasi penghapusan.

6. Struktur Direktori Views Admin
Setelah semua view dibuat, struktur direktori app/Views menjadi:
app/Views/
admin/
admin_post_list.php ← Daftar semua postingan (Read)
admin_post_create.php ← Form tambah postingan (Create)
admin_post_update.php ← Form ubah postingan (Update)
home.php
about.php
contact.php
faqs.php
post.php
post_detail.php ← Juga digunakan untuk preview admin
Mapping Route Admin ke Controller dan View:
| Route | Method | Aksi |
|---|---|---|
/admin/post | PostAdmin::index | Menampilkan daftar artikel |
/admin/post/(:segment)/preview | PostAdmin::preview/$1 | Pratinjau artikel |
/admin/post/new | PostAdmin::create | Form tambah & simpan artikel |
/admin/post/(:segment)/edit | PostAdmin::edit/$1 | Form ubah & simpan artikel |
/admin/post/(:segment)/delete | PostAdmin::delete/$1 | Hapus artikel |
7. Ringkasan
Praktik ini menerapkan CRUD (Create, Read, Update, Delete) pada panel admin untuk mengelola data postingan blog menggunakan CodeIgniter 4:
- Route Group — semua route admin dikelompokkan dalam group
adminagar terorganisir. - Read — method
index()menampilkan seluruh daftar artikel denganfindAll(), danpreview($id)menampilkan detail satu artikel denganfirst(). - Create — method
create()melakukan validasi input, menyimpan data baru ke database, dan menghasilkanslugotomatis dari judul. - Update — method
edit($id)mengambil data lama, memvalidasi perubahan, lalu menyimpan data yang diperbarui. - Delete — method
delete($id)menghapus data berdasarkan ID, dilengkapi modal konfirmasi Bootstrap sebelum eksekusi. - Validasi — setiap proses create dan update dilengkapi validasi menggunakan
\Config\Services::validation()untuk memastikan data yang masuk sesuai aturan.
Dengan menguasai alur kerja CRUD ini, aplikasi web dapat mengelola data secara efisien dan responsif sesuai kebutuhan pengguna.


