- Sistem Informasi Perpustakaan CodeIgniter 4: Rancangan Aplikasi SIPUS
- Sistem Informasi Perpustakaan CodeIgniter 4: Setup dan Konfigurasi Database
- Sistem Informasi Perpustakaan CodeIgniter 4: CRUD Kategori Buku
- Membuat Layout Utama SIPUS dengan CodeIgniter 4: Navbar, Sidebar, dan Main Layout
- Membuat CRUD Buku dengan CodeIgniter 4 dan Bootstrap 5 pada Aplikasi SIPUS
- Membuat Dashboard SIPUS dengan CodeIgniter 4 dan Bootstrap 5
- Membuat CRUD Member dengan CodeIgniter 4 dan Bootstrap 5 pada Aplikasi SIPUS
mwi – Halo sobat pembelajar mwi, apa kabarnya hari ini, yuk kita lanjut berlajarnya pada materi pembuatakan Aplikasi Sistem Perpustakaan (SIPUS). Sebelum lanjut ke CRUD Member kita bisa mengupdate dahulu versi CodeIgniter dari 4.7.5 ke 4.7.6 (terbaru saat artikel ini dibuat), caranya ketikan perintah ini
composer update
Daftar Isi
Pendahuluan
Setelah menyelesaikan Authentication Login dan Logout, langkah berikutnya dalam membangun aplikasi perpustakaan SIPUS adalah membuat modul Member atau Anggota.
CRUD Member CodeIgniter 4 merupakan fitur untuk mengelola data anggota perpustakaan, mulai dari menambahkan anggota, menampilkan daftar anggota, mengubah data, hingga menonaktifkan anggota.
Pada tutorial ini, kita akan menggunakan CodeIgniter 4.7.6, PHP 8.1+, MySQL, dan Bootstrap 5. Kita juga akan menerapkan validasi melalui Model agar aturan validasi tersimpan di satu tempat dan lebih mudah digunakan kembali.
Modul Member akan menjadi bagian penting dari SIPUS karena data anggota akan terhubung dengan proses peminjaman dan pengembalian buku.
1. Tujuan Pembelajaran
Setelah mengikuti tutorial ini, kita akan mampu:
- Membuat Model Member dengan CodeIgniter 4.
- Menyimpan aturan validasi di dalam Model.
- Membuat Controller untuk mengelola data anggota.
- Membuat halaman daftar anggota.
- Membuat formulir tambah dan edit anggota.
- Menggunakan Bootstrap 5 untuk tampilan.
- Mengatur Routes untuk CRUD Member.
- Membatasi akses halaman menggunakan Authentication Filter.
- Menonaktifkan anggota tanpa menghapus riwayat datanya.
2. Struktur Tabel Members
Sebelumnya, kita sudah menyiapkan migration tabel members dengan struktur berikut:
| Field | Tipe Data | Keterangan |
|---|---|---|
| id | INT unsigned | Primary key, auto increment |
| member_code | VARCHAR(30) | Kode anggota, unik |
| name | VARCHAR(150) | Nama anggota |
| class_name | VARCHAR(50) | Kelas, boleh kosong |
| phone | VARCHAR(30) | Nomor telepon, boleh kosong |
| status | VARCHAR(20) | Status anggota, default active |
| created_at | DATETIME | Waktu data dibuat |
| updated_at | DATETIME | Waktu data diperbarui |
Kolom member_code memiliki unique index sehingga setiap anggota harus memiliki kode yang berbeda.
Kolom status menggunakan dua nilai:
active: anggota masih aktif dan dapat melakukan peminjaman.inactive: anggota tidak aktif dan tidak dapat melakukan peminjaman baru.
Kita akan mempertahankan kedua nilai tersebut agar proses peminjaman nantinya dapat memeriksa status anggota dengan mudah.
3. Persiapan Struktur Folder
Kita akan membuat beberapa file berikut:
app/
├── Controllers/
│ └── Members.php
├── Models/
│ └── MemberModel.php
└── Views/
└── members/
├── index.php
└── form.php
Pastikan folder members tersedia di dalam app/Views. Jika belum ada, buat folder tersebut terlebih dahulu.
4. Membuat MemberModel
Model berfungsi sebagai penghubung antara aplikasi dan tabel database.
Pada tutorial ini, kita akan menempatkan aturan validasi di Model. CodeIgniter 4 menyediakan validasi otomatis ketika aplikasi menjalankan method insert() dan update() pada Model. Jika validasi gagal, Model mengembalikan false dan kita dapat mengambil pesan kesalahannya melalui method errors().
Buat file:
app/Models/MemberModel.php
<?php
namespace App\Models;
use CodeIgniter\Model;
class MemberModel extends Model
{
protected $table = 'members';
protected $primaryKey = 'id';
protected $returnType = 'array';
protected $allowedFields = [
'member_code',
'name',
'class_name',
'phone',
'status',
];
protected $useTimestamps = true;
protected $dateFormat = 'datetime';
// Aturan validasi data anggota.
protected $validationRules = [
'member_code' => 'required|max_length[30]|is_unique[members.member_code]',
'name' => 'required|min_length[2]|max_length[150]',
'class_name' => 'permit_empty|max_length[50]',
'phone' => 'permit_empty|max_length[30]',
'status' => 'required|in_list[active,inactive]',
];
// Pesan kesalahan validasi.
protected $validationMessages = [
'member_code' => [
'required' => 'Kode anggota wajib diisi.',
'max_length'=> 'Kode anggota maksimal 30 karakter.',
'is_unique' => 'Kode anggota sudah digunakan.',
],
'name' => [
'required' => 'Nama anggota wajib diisi.',
'min_length' => 'Nama anggota minimal 2 karakter.',
'max_length' => 'Nama anggota maksimal 150 karakter.',
],
'class_name' => [
'max_length' => 'Nama kelas maksimal 50 karakter.',
],
'phone' => [
'max_length' => 'Nomor telepon maksimal 30 karakter.',
],
'status' => [
'required' => 'Status anggota wajib dipilih.',
'in_list' => 'Status anggota tidak valid.',
],
];
// Aturan khusus untuk proses update.
public function setRulesForUpdate(int $id): void
{
$this->setValidationRule(
'member_code',
"required|max_length[30]|is_unique[members.member_code,id,{$id}]"
);
}
}
Penjelasan MemberModel
a. Konfigurasi tabel
protected $table = 'members';
protected $primaryKey = 'id';
protected $returnType = 'array';
Ketiga properti tersebut menentukan tabel, primary key, dan bentuk hasil query.
b. Allowed Fields
protected $allowedFields = [
'member_code',
'name',
'class_name',
'phone',
'status',
];
Kita hanya memasukkan kolom yang boleh diisi melalui Model. Kolom id, created_at, dan updated_at tidak perlu dimasukkan ke $allowedFields.
c. Pengaturan timestamp
protected $useTimestamps = true;
protected $dateFormat = 'datetime';
Dengan konfigurasi tersebut, CodeIgniter mengelola created_at dan updated_at secara otomatis. Pastikan kedua kolom tersedia pada tabel.
d. Aturan validasi
Kita menggunakan beberapa aturan berikut:
required: kolom wajib diisi.min_length: menentukan jumlah karakter minimum.max_length: menentukan jumlah karakter maksimum.permit_empty: memperbolehkan kolom kosong.is_unique: memastikan nilai tidak digunakan oleh data lain.in_list: membatasi pilihan nilai yang diperbolehkan.
e. Validasi kode anggota saat update
Pada proses edit, anggota boleh mempertahankan kode miliknya sendiri. Karena itu, kita menggunakan aturan:
is_unique[members.member_code,id,{$id}]
Aturan tersebut mengecualikan data anggota dengan ID yang sedang diperbarui. Dengan demikian, anggota tidak akan mendapatkan pesan duplikat hanya karena mempertahankan kode yang sama.
Kita mengatur aturan khusus tersebut melalui method setRulesForUpdate() agar logika validasi tetap berada di Model.
5. Membuat Members Controller
Controller bertugas menangani request dari pengguna, memanggil Model, dan menentukan halaman yang akan ditampilkan.
Buat file:
app/Controllers/Members.php
Isi dengan kode berikut:
<?php
namespace App\Controllers;
use App\Models\MemberModel;
class Members extends BaseController
{
// Menampilkan daftar anggota.
public function index()
{
$model = new MemberModel();
return view('members/index', [
'members' => $model->orderBy('id', 'DESC')->findAll(),
]);
}
// Menampilkan formulir tambah anggota.
public function new()
{
return view('members/form', [
'member' => null,
'errors' => session()->getFlashdata('errors') ?? [],
]);
}
// Menyimpan anggota baru.
public function create()
{
$model = new MemberModel();
$data = [
'member_code' => trim($this->request->getPost('member_code')),
'name' => trim($this->request->getPost('name')),
'class_name' => trim((string) $this->request->getPost('class_name')) ?: null,
'phone' => trim((string) $this->request->getPost('phone')) ?: null,
'status' => $this->request->getPost('status') ?: 'active',
];
// Model menjalankan validasi secara otomatis.
if ($model->insert($data) === false) {
return redirect()->back()
->withInput()
->with('errors', $model->errors());
}
return redirect()->to('/members')
->with('success', 'Anggota berhasil ditambahkan.');
}
// Menampilkan formulir edit anggota.
public function edit($id)
{
$model = new MemberModel();
$member = $model->find($id);
if (! $member) {
return redirect()->to('/members')
->with('error', 'Data anggota tidak ditemukan.');
}
return view('members/form', [
'member' => $member,
'errors' => session()->getFlashdata('errors') ?? [],
]);
}
// Memperbarui data anggota.
public function update($id)
{
$model = new MemberModel();
$member = $model->find($id);
if (! $member) {
return redirect()->to('/members')
->with('error', 'Data anggota tidak ditemukan.');
}
// Gunakan aturan unik yang mengecualikan ID anggota saat ini.
$model->setRulesForUpdate((int) $id);
$data = [
'member_code' => trim($this->request->getPost('member_code')),
'name' => trim($this->request->getPost('name')),
'class_name' => trim((string) $this->request->getPost('class_name')) ?: null,
'phone' => trim((string) $this->request->getPost('phone')) ?: null,
'status' => $this->request->getPost('status'),
];
if ($model->update($id, $data) === false) {
return redirect()->back()
->withInput()
->with('errors', $model->errors());
}
return redirect()->to('/members')
->with('success', 'Data anggota berhasil diperbarui.');
}
// Menonaktifkan anggota.
public function delete($id)
{
$model = new MemberModel();
$member = $model->find($id);
if (! $member) {
return redirect()->to('/members')
->with('error', 'Data anggota tidak ditemukan.');
}
if ($member['status'] === 'inactive') {
return redirect()->to('/members')
->with('error', 'Anggota sudah berstatus tidak aktif.');
}
// Tidak menghapus data secara permanen.
// Kita hanya mengubah status anggota.
if ($model->update($id, [
'status' => 'inactive',
]) === false) {
return redirect()->to('/members')
->with('error', 'Anggota gagal dinonaktifkan.');
}
return redirect()->to('/members')
->with('success', 'Anggota berhasil dinonaktifkan.');
}
}
Penjelasan Members Controller
Controller memiliki enam method utama.
| Method | Fungsi |
|---|---|
index() | Menampilkan daftar anggota |
new() | Menampilkan formulir tambah anggota |
create() | Menyimpan anggota baru |
edit($id) | Menampilkan formulir edit |
update($id) | Memperbarui data anggota |
delete($id) | Menonaktifkan anggota |
Mengapa Controller tidak menulis aturan validasi?
Karena kita sudah menempatkan aturan validasi pada MemberModel. Controller cukup memanggil insert() atau update(). Model akan menjalankan validasi sebelum menyimpan data.
Jika validasi gagal, Controller mengambil pesan kesalahan melalui:
$model->errors()
Kemudian Controller mengirimkan pesan tersebut kembali ke halaman formulir.
Mengapa method delete hanya mengubah status?
Data anggota nantinya menjadi bagian dari riwayat transaksi peminjaman. Jika kita menghapus anggota secara permanen, relasi transaksi dapat terganggu.
Karena itu, kita menggunakan status inactive sebagai pengganti penghapusan permanen. Anggota yang sudah tidak aktif tetap tersimpan di database.
6. Membuat Halaman Daftar Anggota
Sekarang kita membuat halaman untuk menampilkan seluruh anggota.
Buat file:
app/Views/members/index.php
<?= $this->extend('layouts/main') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h3 class="mb-1">Data Anggota</h3>
<p class="text-muted mb-0">
Kelola data anggota perpustakaan SIPUS.
</p>
</div>
<a href="<?= site_url('members/new') ?>"
class="btn btn-primary">
Tambah Anggota
</a>
</div>
<?php if (session()->getFlashdata('success')): ?>
<div class="alert alert-success">
<?= esc(session()->getFlashdata('success')) ?>
</div>
<?php endif; ?>
<?php if (session()->getFlashdata('error')): ?>
<div class="alert alert-danger">
<?= esc(session()->getFlashdata('error')) ?>
</div>
<?php endif; ?>
<div class="card border-0 shadow-sm">
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered table-hover align-middle">
<thead class="table-light">
<tr>
<th width="60">No.</th>
<th>Kode Anggota</th>
<th>Nama</th>
<th>Kelas</th>
<th>Telepon</th>
<th>Status</th>
<th width="160">Aksi</th>
</tr>
</thead>
<tbody>
<?php if (! empty($members)): ?>
<?php $no = 1; ?>
<?php foreach ($members as $member): ?>
<tr>
<td><?= $no++ ?></td>
<td>
<?= esc($member['member_code']) ?>
</td>
<td>
<?= esc($member['name']) ?>
</td>
<td>
<?= esc($member['class_name'] ?? '-') ?>
</td>
<td>
<?= esc($member['phone'] ?? '-') ?>
</td>
<td>
<?php if ($member['status'] === 'active'): ?>
<span class="badge bg-success">
Aktif
</span>
<?php else: ?>
<span class="badge bg-secondary">
Tidak Aktif
</span>
<?php endif; ?>
</td>
<td>
<a href="<?= site_url('members/edit/' . $member['id']) ?>"
class="btn btn-warning btn-sm">
Edit
</a>
<?php if ($member['status'] === 'active'): ?>
<form
action="<?= site_url('members/delete/' . $member['id']) ?>"
method="post"
class="d-inline"
onsubmit="return confirm('Nonaktifkan anggota ini?')"
>
<?= csrf_field() ?>
<button type="submit"
class="btn btn-danger btn-sm">
Nonaktifkan
</button>
</form>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="7" class="text-center text-muted">
Belum ada data anggota.
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<?= $this->endSection() ?>
Penjelasan halaman daftar anggota
Halaman ini menampilkan data anggota dalam bentuk tabel.
Kita menggunakan foreach untuk membaca seluruh data anggota yang dikirimkan Controller.
Kolom yang ditampilkan meliputi:
- Nomor urut.
- Kode anggota.
- Nama anggota.
- Kelas.
- Nomor telepon.
- Status.
- Tombol aksi.
Kita juga menambahkan tombol Edit dan Nonaktifkan.
Tombol Nonaktifkan menggunakan formulir POST dan CSRF token. Dengan begitu, perubahan status tidak dilakukan melalui tautan GET.
7. Membuat Form Tambah dan Edit Anggota
Selanjutnya, kita membuat satu formulir yang dapat digunakan untuk menambah dan mengedit data anggota.
Buat file:
app/Views/members/form.php
<?= $this->extend('layouts/main') ?>
<?= $this->section('content') ?>
<?php
$member = $member ?? null;
$errors = $errors ?? [];
$isEdit = $member !== null;
?>
<div class="container-fluid">
<div class="mb-4">
<h3>
<?= $isEdit ? 'Edit Anggota' : 'Tambah Anggota' ?>
</h3>
<p class="text-muted">
<?= $isEdit
? 'Perbarui informasi anggota perpustakaan.'
: 'Masukkan informasi anggota baru.' ?>
</p>
</div>
<?php if (! empty($errors)): ?>
<div class="alert alert-danger">
<strong>Periksa kembali data berikut:</strong>
<ul class="mb-0 mt-2">
<?php foreach ($errors as $error): ?>
<li><?= esc($error) ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<div class="card border-0 shadow-sm">
<div class="card-body">
<form
action="<?= $isEdit
? site_url('members/update/' . $member['id'])
: site_url('members/create') ?>"
method="post"
>
<?= csrf_field() ?>
<div class="mb-3">
<label class="form-label">Kode Anggota</label>
<input
type="text"
name="member_code"
class="form-control"
value="<?= esc(old('member_code', $member['member_code'] ?? '')) ?>"
maxlength="30"
required
>
</div>
<div class="mb-3">
<label class="form-label">Nama Anggota</label>
<input
type="text"
name="name"
class="form-control"
value="<?= esc(old('name', $member['name'] ?? '')) ?>"
maxlength="150"
required
>
</div>
<div class="mb-3">
<label class="form-label">Kelas</label>
<input
type="text"
name="class_name"
class="form-control"
value="<?= esc(old('class_name', $member['class_name'] ?? '')) ?>"
maxlength="50"
placeholder="Contoh: X IPA 1"
>
</div>
<div class="mb-3">
<label class="form-label">Nomor Telepon</label>
<input
type="text"
name="phone"
class="form-control"
value="<?= esc(old('phone', $member['phone'] ?? '')) ?>"
maxlength="30"
placeholder="Contoh: 081234567890"
>
</div>
<div class="mb-3">
<label class="form-label">Status Anggota</label>
<?php
$status = old(
'status',
$member['status'] ?? 'active'
);
?>
<select name="status" class="form-select" required>
<option value="active"
<?= $status === 'active' ? 'selected' : '' ?>>
Aktif
</option>
<option value="inactive"
<?= $status === 'inactive' ? 'selected' : '' ?>>
Tidak Aktif
</option>
</select>
</div>
<div class="d-flex gap-2">
<button type="submit" class="btn btn-primary">
<?= $isEdit ? 'Simpan Perubahan' : 'Simpan Anggota' ?>
</button>
<a href="<?= site_url('members') ?>"
class="btn btn-secondary">
Kembali
</a>
</div>
</form>
</div>
</div>
</div>
<?= $this->endSection() ?>
Penjelasan Form
Kita menggunakan satu file untuk dua kebutuhan.
Ketika Controller mengirimkan:
'member' => null
halaman akan menampilkan formulir tambah anggota.
Sebaliknya, ketika Controller mengirimkan data anggota, halaman akan menampilkan formulir edit.
Kita menggunakan helper old() agar data yang sudah dimasukkan pengguna tetap muncul ketika validasi gagal.
8. Mengatur Routes Member
Sekarang kita perlu mendaftarkan URL untuk setiap proses CRUD.
Buka:
app/Config/Routes.php
Tambahkan route berikut ke dalam group auth yang sudah dibuat pada tutorial Authentication:
// Members
$routes->get('members', 'Members::index');
$routes->get('members/new', 'Members::new');
$routes->post('members/create', 'Members::create');
$routes->get('members/edit/(:num)', 'Members::edit/$1');
$routes->post('members/update/(:num)', 'Members::update/$1');
$routes->post('members/delete/(:num)', 'Members::delete/$1');
Pastikan route tersebut berada di dalam:
$routes->group('', ['filter' => 'auth'], static function ($routes) {
// Route dashboard, kategori, buku, dan anggota.
});
Dengan demikian, pengguna harus login terlebih dahulu sebelum mengakses halaman anggota.
Perhatikan penggunaan $1 pada route edit, update, dan delete. Parameter tersebut meneruskan ID anggota dari URL ke Controller.
9. Menambahkan Menu Anggota pada Sidebar
Agar pengguna dapat membuka halaman anggota melalui navigasi SIPUS, kita perlu menambahkan menu Anggota.
Buka:
app/Views/layouts/sidebar.php
Tambahkan menu berikut pada daftar navigasi:
<a href="<?= site_url('members') ?>"
class="nav-link <?= service('uri')->getSegment(1) === 'members' ? 'active' : '' ?>">
Anggota
</a>
Sesuaikan posisi menu dengan susunan navigasi yang sudah tersedia.
Sekarang sidebar SIPUS memiliki menu Dashboard, Kategori, Buku, dan Anggota.
10. Menguji CRUD Member
Setelah semua file selesai, jalankan aplikasi:
php spark serve
Pastikan pengguna sudah login, kemudian buka:
http://localhost:8080/members
Lakukan pengujian berikut:
| No. | Pengujian | Hasil yang Diharapkan |
|---|---|---|
| 1 | Membuka halaman anggota | Daftar anggota tampil |
| 2 | Menambahkan anggota baru | Data tersimpan |
| 3 | Mengisi kode anggota yang sama | Validasi menolak data |
| 4 | Mengosongkan nama anggota | Validasi menolak data |
| 5 | Mengisi nama terlalu panjang | Validasi menolak data |
| 6 | Mengedit anggota | Data berhasil diperbarui |
| 7 | Mempertahankan kode anggota saat edit | Tidak dianggap duplikat |
| 8 | Mengganti kode dengan kode anggota lain | Validasi menolak data |
| 9 | Menonaktifkan anggota | Status berubah menjadi inactive |
| 10 | Membuka halaman anggota tanpa login | Dialihkan ke halaman login |
Contoh data pengujian
| Kode Anggota | Nama | Kelas | Telepon | Status |
|---|---|---|---|---|
| AGT001 | Andi Saputra | X IPA 1 | 081234567890 | active |
| AGT002 | Budi Santoso | X IPA 2 | 081234567891 | active |
| AGT003 | Citra Lestari | XI IPS 1 | 081234567892 | active |
11. Memahami Hubungan Member dengan Transaksi Peminjaman
Modul Member bukan hanya tempat menyimpan identitas anggota. Nantinya, tabel members akan terhubung dengan tabel loans.
Gambaran relasinya:
MEMBERS
|
| id
|
| 1
|
| N
LOANS
Satu anggota dapat memiliki beberapa transaksi peminjaman.
Ketika kita membangun modul peminjaman, aplikasi harus memeriksa status anggota terlebih dahulu. Hanya anggota dengan status active yang boleh melakukan peminjaman baru.
Sementara itu, anggota yang sudah memiliki riwayat transaksi tetap tersimpan meskipun statusnya berubah menjadi inactive.
12. Kesimpulan
Pada tutorial ini, kita sudah membuat CRUD Member untuk aplikasi perpustakaan SIPUS menggunakan CodeIgniter 4 dan Bootstrap 5.
Kita telah membuat MemberModel, Members Controller, halaman daftar anggota, formulir tambah dan edit, serta Routes.
Selain itu, kita menerapkan validasi melalui Model agar aturan validasi lebih terstruktur. Kita juga menggunakan status active dan inactive untuk menjaga data anggota tanpa menghapus riwayatnya.
Dengan selesainya modul Member, SIPUS sudah memiliki tiga data utama yang saling mendukung, yaitu Kategori, Buku, dan Anggota.
13. Latihan untuk Pembaca
Untuk memperdalam pemahaman, coba kerjakan latihan berikut:
- Tambahkan pencarian anggota berdasarkan nama atau kode anggota.
- Tambahkan filter untuk menampilkan anggota aktif dan tidak aktif.
- Tambahkan pagination agar daftar anggota tidak menampilkan seluruh data sekaligus.
- Buat fitur untuk mengaktifkan kembali anggota yang berstatus inactive.
- Buat validasi agar kode anggota hanya menerima huruf dan angka.
14. Tutorial Selanjutnya
Setelah modul Member selesai, kita akan melanjutkan pembangunan SIPUS dengan membuat CRUD Peminjaman Buku.
Pada modul tersebut, kita akan mempelajari relasi antara anggota, buku, dan transaksi. Kita juga akan menyiapkan proses peminjaman yang dapat memuat beberapa buku dalam satu transaksi, memeriksa stok, dan mengurangi stok hanya ketika transaksi berhasil, semangan selalu by ms.
Link penting: