Membuat CRUD Member dengan CodeIgniter 4 dan Bootstrap 5 pada Aplikasi SIPUS

By | November 14, 2026
Dashboard SIPUS
This entry is part 7 of 7 in the series Sistem Informasi Perpustakaan CodeIgniter 4

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

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:

  1. Membuat Model Member dengan CodeIgniter 4.
  2. Menyimpan aturan validasi di dalam Model.
  3. Membuat Controller untuk mengelola data anggota.
  4. Membuat halaman daftar anggota.
  5. Membuat formulir tambah dan edit anggota.
  6. Menggunakan Bootstrap 5 untuk tampilan.
  7. Mengatur Routes untuk CRUD Member.
  8. Membatasi akses halaman menggunakan Authentication Filter.
  9. Menonaktifkan anggota tanpa menghapus riwayat datanya.

2. Struktur Tabel Members

Sebelumnya, kita sudah menyiapkan migration tabel members dengan struktur berikut:

FieldTipe DataKeterangan
idINT unsignedPrimary key, auto increment
member_codeVARCHAR(30)Kode anggota, unik
nameVARCHAR(150)Nama anggota
class_nameVARCHAR(50)Kelas, boleh kosong
phoneVARCHAR(30)Nomor telepon, boleh kosong
statusVARCHAR(20)Status anggota, default active
created_atDATETIMEWaktu data dibuat
updated_atDATETIMEWaktu 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.

MethodFungsi
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.PengujianHasil yang Diharapkan
1Membuka halaman anggotaDaftar anggota tampil
2Menambahkan anggota baruData tersimpan
3Mengisi kode anggota yang samaValidasi menolak data
4Mengosongkan nama anggotaValidasi menolak data
5Mengisi nama terlalu panjangValidasi menolak data
6Mengedit anggotaData berhasil diperbarui
7Mempertahankan kode anggota saat editTidak dianggap duplikat
8Mengganti kode dengan kode anggota lainValidasi menolak data
9Menonaktifkan anggotaStatus berubah menjadi inactive
10Membuka halaman anggota tanpa loginDialihkan ke halaman login

Contoh data pengujian

Kode AnggotaNamaKelasTeleponStatus
AGT001Andi SaputraX IPA 1081234567890active
AGT002Budi SantosoX IPA 2081234567891active
AGT003Citra LestariXI IPS 1081234567892active

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:

  1. Tambahkan pencarian anggota berdasarkan nama atau kode anggota.
  2. Tambahkan filter untuk menampilkan anggota aktif dan tidak aktif.
  3. Tambahkan pagination agar daftar anggota tidak menampilkan seluruh data sekaligus.
  4. Buat fitur untuk mengaktifkan kembali anggota yang berstatus inactive.
  5. 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:

  1. User Guide CodeIgniter
  2. Bootstrap 5.3

Tinggalkan Balasan

Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai *

Situs ini menggunakan Akismet untuk mengurangi spam. Pelajari bagaimana data komentar Anda diproses