đŸ’ģ Pemrograman Web 2
🎓 Pertemuan
Pertemuan 7: PHP-MySQL Integration (CRUD Native)

MODUL PEMROGRAMAN WEBSITE 2

Mata Kuliah: Pemrograman Website 2
Kode MK: INF2419
SKS: 3 (Praktikum)
Semester: Genap 2025/2026
Program Studi: Informatika
Fakultas: FEBI / Saintek
Universitas: UIN K.H. Abdurrahman Wahid Pekalongan

Dosen Pengampu: Mohammad Reza Maulana, M.Kom
NIP: 199110082025051002

Pertemuan: 7 dari 16
Durasi: 150 menit (3 × 50 menit)
Studi Kasus Berkelanjutan: Sistem Manajemen Perpustakaan


PERTEMUAN 7

PHP-MYSQL INTEGRATION (CRUD NATIVE)

A. INFORMASI PERTEMUAN

AspekKeterangan
Capaian Pembelajaran Lulusan (CPL)CPL04: Menguasai konsep teoretis bidang ilmu komputer/informatika dan mampu memformulasikan penyelesaian masalah prosedural
Capaian Pembelajaran Mata Kuliah (CPMK)CPMK04.1: Memahami konsep dasar pengembangan web backend menggunakan PHP dan database
Sub-CPMKSub-CPMK04.1.4: Mampu menguraikan dan menerapkan konsep CRUD pada aplikasi web
Indikator PencapaianMahasiswa mampu:
1. Membuat koneksi database menggunakan PHP mysqli
2. Menggunakan prepared statements untuk keamanan
3. Mengimplementasikan CREATE (Insert data buku)
4. Mengimplementasikan READ (Menampilkan list buku)
5. Mengimplementasikan UPDATE (Edit data buku)
6. Mengimplementasikan DELETE (Hapus data buku)
7. Menangani error database dengan baik
8. Membuat aplikasi CRUD buku lengkap yang terintegrasi
Alokasi Waktuâ€ĸ Teori: 60 menit
â€ĸ Praktikum: 90 menit
â€ĸ Total: 150 menit (3 × 50 menit)

B. PENDAHULUAN

1. Deskripsi Singkat

Pertemuan ketujuh ini merupakan kulminasi dari pembelajaran PHP-MySQL di mana semua konsep yang telah dipelajari (PHP dasar, kontrol alur, array, function, form handling, dan database) akan diintegrasikan menjadi aplikasi CRUD (Create, Read, Update, Delete) yang lengkap. Mahasiswa akan belajar cara menghubungkan PHP dengan MySQL menggunakan mysqli extension, menerapkan prepared statements untuk keamanan, dan membangun sistem manajemen buku perpustakaan yang fungsional.

2. Keterkaitan dengan Pertemuan Lain

Pertemuan ini adalah integrasi dari semua pertemuan sebelumnya:

  • Pertemuan 2-3: PHP dasar dan kontrol alur digunakan untuk logika aplikasi
  • Pertemuan 4: Array dan function untuk mengelola data dan modularisasi code
  • Pertemuan 5: Form handling untuk input dan update data
  • Pertemuan 6: Database MySQL yang sudah dibuat akan digunakan
  • Pertemuan 8: UTS akan menguji pemahaman CRUD native ini
  • Pertemuan 9-16: Konsep yang sama diterapkan dalam Laravel dengan cara yang lebih modern

3. Manfaat Pembelajaran

  1. Mampu membangun aplikasi web dinamis yang tersimpan permanen di database
  2. Memahami cara kerja integrasi frontend-backend-database
  3. Dapat mengimplementasikan operasi CRUD secara lengkap
  4. Memiliki skill fundamental sebelum belajar framework Laravel
  5. Memahami pentingnya keamanan dalam aplikasi web (SQL injection prevention)

4. Relevansi dengan Studi Kasus

Dalam sistem perpustakaan, CRUD digunakan untuk:

  • Create: Menambah buku baru ke koleksi perpustakaan
  • Read: Menampilkan daftar buku dan detailnya
  • Update: Mengubah informasi buku (harga, stok, dll)
  • Delete: Menghapus buku yang sudah tidak diperlukan
  • Integration: Semua operasi tersimpan permanen di database

C. MATERI TEORI

1. PHP MySQLi Extension

a. Apa itu MySQLi?

MySQLi (MySQL Improved) adalah extension PHP untuk berinteraksi dengan database MySQL.

Keuntungan MySQLi:

  • Support MySQL 4.1+
  • Object-oriented dan procedural interface
  • Prepared statements (security)
  • Multiple statements
  • Transaction support
  • Better performance

MySQLi vs MySQL (old):

AspekMySQL (deprecated)MySQLi
StatusDeprecated PHP 5.5+Aktif
OOP SupportTidakYa
Prepared StatementsTidakYa
SecurityRendahTinggi
RecommendationJangan dipakaiRecommended

Alternative: PDO (PHP Data Objects) - lebih fleksibel tapi syntax berbeda.

b. MySQLi Procedural vs OOP

Procedural:

<?php
$conn = mysqli_connect("localhost", "root", "", "perpustakaan");
$result = mysqli_query($conn, "SELECT * FROM buku");
mysqli_close($conn);
?>

Object-Oriented:

<?php
$conn = new mysqli("localhost", "root", "", "perpustakaan");
$result = $conn->query("SELECT * FROM buku");
$conn->close();
?>

Dalam modul ini: Kita akan gunakan OOP style karena lebih modern dan clean.


2. Koneksi Database

a. Membuat Koneksi

Sintaks Dasar:

<?php
$servername = "localhost";
$username = "root";
$password = "";
$database = "perpustakaan";
 
// Buat koneksi
$conn = new mysqli($servername, $username, $password, $database);
 
// Cek koneksi
if ($conn->connect_error) {
    die("Koneksi gagal: " . $conn->connect_error);
}
 
echo "Koneksi berhasil";
?>

Parameter Koneksi:

  • servername: Host database (localhost untuk lokal)
  • username: Username MySQL (default: root)
  • password: Password MySQL (default: kosong di XAMPP)
  • database: Nama database yang akan digunakan

b. File Konfigurasi Terpisah

Best Practice: Pisahkan koneksi ke file terpisah untuk reusability.

Buat file config/database.php:

<?php
// Konfigurasi database
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'root');
define('DB_PASSWORD', '');
define('DB_NAME', 'perpustakaan');
 
// Buat koneksi
$conn = new mysqli(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
 
// Set charset UTF-8
$conn->set_charset("utf8mb4");
 
// Cek koneksi
if ($conn->connect_error) {
    die("Koneksi database gagal: " . $conn->connect_error);
}
 
// Fungsi helper untuk close connection
function closeConnection() {
    global $conn;
    if ($conn) {
        $conn->close();
    }
}
?>

Menggunakan koneksi:

<?php
require_once 'config/database.php';
 
// Sekarang $conn siap digunakan
$result = $conn->query("SELECT * FROM buku");
 
// ... code lainnya
 
closeConnection();
?>

c. Error Handling Koneksi

<?php
try {
    $conn = new mysqli("localhost", "root", "", "perpustakaan");
    
    if ($conn->connect_error) {
        throw new Exception("Koneksi gagal: " . $conn->connect_error);
    }
    
    $conn->set_charset("utf8mb4");
    
} catch (Exception $e) {
    // Log error (production)
    error_log($e->getMessage());
    
    // Tampilkan pesan user-friendly
    die("Maaf, terjadi kesalahan koneksi database. Silakan coba lagi nanti.");
}
?>

3. Prepared Statements

a. Kenapa Prepared Statements?

Masalah SQL Injection:

<?php
// ❌ VULNERABLE CODE - JANGAN DIPAKAI!
$judul = $_POST['judul'];
$query = "SELECT * FROM buku WHERE judul = '$judul'";
$result = $conn->query($query);
 
// User bisa input: ' OR '1'='1
// Query jadi: SELECT * FROM buku WHERE judul = '' OR '1'='1'
// Semua data bocor!
?>

Solusi: Prepared Statements

<?php
// ✅ AMAN dengan prepared statement
$judul = $_POST['judul'];
$stmt = $conn->prepare("SELECT * FROM buku WHERE judul = ?");
$stmt->bind_param("s", $judul);
$stmt->execute();
$result = $stmt->get_result();
 
// Input user otomatis di-escape
// Tidak mungkin terjadi SQL injection
?>

b. Cara Kerja Prepared Statements

3 Langkah:

  1. Prepare: Buat template query dengan placeholder (?)
  2. Bind: Ikat variabel ke placeholder dengan tipe data
  3. Execute: Jalankan query

Tipe Data Bind:

  • "s" - String
  • "i" - Integer
  • "d" - Double/Decimal
  • "b" - Blob (binary)

Contoh Multiple Parameters:

<?php
$stmt = $conn->prepare("INSERT INTO buku (judul, pengarang, harga, stok) VALUES (?, ?, ?, ?)");
$stmt->bind_param("ssdi", $judul, $pengarang, $harga, $stok);
//                  ^^^^
//                  s = string (judul)
//                  s = string (pengarang)
//                  d = double (harga)
//                  i = integer (stok)
 
$stmt->execute();
?>

c. SELECT dengan Prepared Statement

<?php
// Prepare
$stmt = $conn->prepare("SELECT * FROM buku WHERE kategori = ?");
 
// Bind
$kategori = "Programming";
$stmt->bind_param("s", $kategori);
 
// Execute
$stmt->execute();
 
// Get result
$result = $stmt->get_result();
 
// Fetch data
while ($row = $result->fetch_assoc()) {
    echo $row['judul'] . "<br />";
}
 
$stmt->close();
?>

d. INSERT dengan Prepared Statement

<?php
$stmt = $conn->prepare("INSERT INTO buku (kode_buku, judul, kategori, pengarang, penerbit, tahun_terbit, harga, stok) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
 
$stmt->bind_param("ssssssdi", 
    $kode_buku, 
    $judul, 
    $kategori, 
    $pengarang, 
    $penerbit, 
    $tahun_terbit, 
    $harga, 
    $stok
);
 
// Set values
$kode_buku = "BK-009";
$judul = "React Native Development";
$kategori = "Programming";
$pengarang = "Ahmad Yani";
$penerbit = "Informatika";
$tahun_terbit = "2024";
$harga = 135000;
$stok = 10;
 
// Execute
if ($stmt->execute()) {
    echo "Data berhasil disimpan. ID: " . $stmt->insert_id;
} else {
    echo "Error: " . $stmt->error;
}
 
$stmt->close();
?>

e. UPDATE dengan Prepared Statement

<?php
$stmt = $conn->prepare("UPDATE buku SET harga = ?, stok = ? WHERE id_buku = ?");
 
$stmt->bind_param("dii", $harga, $stok, $id_buku);
 
$harga = 80000;
$stok = 15;
$id_buku = 1;
 
if ($stmt->execute()) {
    echo "Data berhasil diupdate. Rows affected: " . $stmt->affected_rows;
} else {
    echo "Error: " . $stmt->error;
}
 
$stmt->close();
?>

f. DELETE dengan Prepared Statement

<?php
$stmt = $conn->prepare("DELETE FROM buku WHERE id_buku = ?");
 
$stmt->bind_param("i", $id_buku);
 
$id_buku = 5;
 
if ($stmt->execute()) {
    if ($stmt->affected_rows > 0) {
        echo "Data berhasil dihapus";
    } else {
        echo "Data tidak ditemukan";
    }
} else {
    echo "Error: " . $stmt->error;
}
 
$stmt->close();
?>

4. CRUD Operations

a. CREATE (Insert)

Flow CREATE:

  1. User mengisi form
  2. Data dikirim via POST
  3. Validasi input
  4. Sanitasi data
  5. Prepared statement INSERT
  6. Redirect dengan success message

Contoh Lengkap:

<?php
require_once 'config/database.php';
 
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    // 1. Ambil data dari form
    $kode_buku = trim($_POST['kode_buku']);
    $judul = trim($_POST['judul']);
    $kategori = trim($_POST['kategori']);
    $pengarang = trim($_POST['pengarang']);
    $penerbit = trim($_POST['penerbit']);
    $tahun = trim($_POST['tahun']);
    $harga = trim($_POST['harga']);
    $stok = trim($_POST['stok']);
    
    // 2. Validasi
    $errors = [];
    
    if (empty($judul)) {
        $errors[] = "Judul wajib diisi";
    }
    
    if (!is_numeric($harga) || $harga < 0) {
        $errors[] = "Harga tidak valid";
    }
    
    // 3. Jika valid, insert
    if (count($errors) == 0) {
        $stmt = $conn->prepare("INSERT INTO buku (kode_buku, judul, kategori, pengarang, penerbit, tahun_terbit, harga, stok) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
        
        $stmt->bind_param("sssssidi", $kode_buku, $judul, $kategori, $pengarang, $penerbit, $tahun, $harga, $stok);
        
        if ($stmt->execute()) {
            header("Location: index.php?success=Data berhasil ditambahkan");
            exit();
        } else {
            $errors[] = "Error database: " . $stmt->error;
        }
        
        $stmt->close();
    }
}
 
closeConnection();
?>

b. READ (Select)

Flow READ:

  1. Query SELECT dari database
  2. Fetch hasil ke array
  3. Loop dan tampilkan dalam HTML
  4. Handle jika data kosong

Contoh SELECT All:

<?php
require_once 'config/database.php';
 
// Query semua buku
$query = "SELECT * FROM buku ORDER BY created_at DESC";
$result = $conn->query($query);
 
if ($result->num_rows > 0) {
    while ($row = $result->fetch_assoc()) {
        echo "<tr>";
        echo "<td>" . htmlspecialchars($row['kode_buku']) . "</td>";
        echo "<td>" . htmlspecialchars($row['judul']) . "</td>";
        echo "<td>Rp " . number_format($row['harga'], 0, ',', '.') . "</td>";
        echo "<td>" . $row['stok'] . "</td>";
        echo "</tr>";
    }
} else {
    echo "<tr><td colspan='4'>Tidak ada data</td></tr>";
}
 
closeConnection();
?>

Contoh SELECT by ID:

<?php
$id_buku = (int)$_GET['id'];
 
$stmt = $conn->prepare("SELECT * FROM buku WHERE id_buku = ?");
$stmt->bind_param("i", $id_buku);
$stmt->execute();
$result = $stmt->get_result();
 
if ($result->num_rows > 0) {
    $buku = $result->fetch_assoc();
    // Gunakan data $buku
} else {
    header("Location: index.php?error=Data tidak ditemukan");
    exit();
}
 
$stmt->close();
?>

c. UPDATE (Edit)

Flow UPDATE:

  1. Ambil data by ID (untuk populate form)
  2. Tampilkan form dengan data yang ada
  3. Submit form
  4. Validasi input
  5. UPDATE database
  6. Redirect

Contoh:

<?php
require_once 'config/database.php';
 
$id_buku = (int)$_GET['id'];
 
// Ambil data untuk form
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
    $stmt = $conn->prepare("SELECT * FROM buku WHERE id_buku = ?");
    $stmt->bind_param("i", $id_buku);
    $stmt->execute();
    $result = $stmt->get_result();
    $buku = $result->fetch_assoc();
    $stmt->close();
}
 
// Proses update
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $judul = trim($_POST['judul']);
    $harga = trim($_POST['harga']);
    $stok = trim($_POST['stok']);
    
    // Validasi...
    
    $stmt = $conn->prepare("UPDATE buku SET judul = ?, harga = ?, stok = ? WHERE id_buku = ?");
    $stmt->bind_param("sdii", $judul, $harga, $stok, $id_buku);
    
    if ($stmt->execute()) {
        header("Location: index.php?success=Data berhasil diupdate");
        exit();
    }
    
    $stmt->close();
}
 
closeConnection();
?>

d. DELETE (Hapus)

Flow DELETE:

  1. Ambil ID dari URL/POST
  2. Konfirmasi delete (JavaScript/PHP)
  3. DELETE dari database
  4. Redirect

Contoh:

<?php
require_once 'config/database.php';
 
if (isset($_GET['id'])) {
    $id_buku = (int)$_GET['id'];
    
    // Cek apakah buku ada
    $stmt = $conn->prepare("SELECT judul FROM buku WHERE id_buku = ?");
    $stmt->bind_param("i", $id_buku);
    $stmt->execute();
    $result = $stmt->get_result();
    
    if ($result->num_rows > 0) {
        $buku = $result->fetch_assoc();
        $stmt->close();
        
        // Delete
        $stmt = $conn->prepare("DELETE FROM buku WHERE id_buku = ?");
        $stmt->bind_param("i", $id_buku);
        
        if ($stmt->execute()) {
            $message = "Buku '{$buku['judul']}' berhasil dihapus";
            header("Location: index.php?success=" . urlencode($message));
            exit();
        }
        
        $stmt->close();
    } else {
        header("Location: index.php?error=Data tidak ditemukan");
        exit();
    }
}
 
closeConnection();
?>

5. Error Handling

a. Database Error Handling

<?php
try {
    $stmt = $conn->prepare("INSERT INTO buku (...) VALUES (...)");
    
    if (!$stmt) {
        throw new Exception("Prepare failed: " . $conn->error);
    }
    
    $stmt->bind_param("...", ...);
    
    if (!$stmt->execute()) {
        throw new Exception("Execute failed: " . $stmt->error);
    }
    
    $success = "Data berhasil disimpan";
    
} catch (Exception $e) {
    // Log error
    error_log($e->getMessage());
    
    // User-friendly message
    $error = "Maaf, terjadi kesalahan. Silakan coba lagi.";
    
    // Development: tampilkan detail
    // $error = $e->getMessage();
}
 
$stmt->close();
?>

b. Transaction untuk Multiple Queries

<?php
// Start transaction
$conn->begin_transaction();
 
try {
    // Query 1: Insert buku
    $stmt1 = $conn->prepare("INSERT INTO buku (...) VALUES (...)");
    $stmt1->bind_param("...", ...);
    $stmt1->execute();
    $id_buku = $stmt1->insert_id;
    $stmt1->close();
    
    // Query 2: Update stok
    $stmt2 = $conn->prepare("UPDATE stok SET jumlah = jumlah + ? WHERE id_buku = ?");
    $stmt2->bind_param("ii", $jumlah, $id_buku);
    $stmt2->execute();
    $stmt2->close();
    
    // Commit jika semua berhasil
    $conn->commit();
    echo "Transaksi berhasil";
    
} catch (Exception $e) {
    // Rollback jika ada error
    $conn->rollback();
    echo "Transaksi gagal: " . $e->getMessage();
}
?>

6. Struktur Folder Project

Struktur yang Recommended:

perpustakaan/
│
├── config/
│   └── database.php          # Koneksi database
│
├── assets/
│   ├── css/
│   │   └── style.css
│   └── js/
│       └── script.js
│
├── includes/
│   ├── header.php            # Header template
│   ├── navbar.php            # Navigation
│   └── footer.php            # Footer template
│
├── modules/
│   └── buku/
│       ├── index.php         # List buku (READ)
│       ├── create.php        # Form tambah (CREATE)
│       ├── store.php         # Process create
│       ├── edit.php          # Form edit (UPDATE)
│       ├── update.php        # Process update
│       └── delete.php        # Process delete (DELETE)
│
├── index.php                 # Homepage
└── README.md

Penjelasan:

  • config: File konfigurasi (database, dll)
  • assets: CSS, JS, images
  • includes: Template yang reusable
  • modules: Fitur terpisah per modul (buku, anggota, dll)

D. PRAKTIKUM

1. Tujuan Praktikum

  1. Setup struktur folder project CRUD
  2. Membuat koneksi database yang aman
  3. Implementasi CREATE (tambah buku)
  4. Implementasi READ (list & detail buku)
  5. Implementasi UPDATE (edit buku)
  6. Implementasi DELETE (hapus buku)
  7. Membuat sistem CRUD buku yang lengkap dan terintegrasi

2. PRAKTIKUM 1: Setup Project & Koneksi Database

Tujuan

Membuat struktur folder project dan koneksi database yang proper.

Langkah-langkah

a. Buat Struktur Folder

Buat folder di C:\xampp\htdocs\perpustakaan\ dengan struktur:

perpustakaan/
├── config/
├── assets/
│   ├── css/
│   └── js/
├── includes/
└── modules/
    └── buku/

b. Buat File Koneksi Database

File: config/database.php

<?php
/**
 * Konfigurasi & Koneksi Database
 * Sistem Manajemen Perpustakaan
 */
 
// Database credentials
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'root');
define('DB_PASSWORD', '');
define('DB_NAME', 'perpustakaan');
 
// Buat koneksi
$conn = new mysqli(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
 
// Set charset ke UTF-8
$conn->set_charset("utf8mb4");
 
// Cek koneksi
if ($conn->connect_error) {
    // Log error (production)
    error_log("Database connection failed: " . $conn->connect_error);
    
    // Tampilkan pesan user-friendly
    die("
    <!DOCTYPE html>
    <html>
    <head>
        <title>Error</title>
        <link href='https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css' rel='stylesheet'>
    </head>
    <body>
        <div class='container mt-5'>
            <div class='alert alert-danger'>
                <h4>Koneksi Database Gagal</h4>
                <p>Maaf, tidak dapat terhubung ke database. Silakan coba lagi nanti.</p>
            </div>
        </div>
    </body>
    </html>
    ");
}
 
/**
 * Fungsi untuk menutup koneksi database
 */
function closeConnection() {
    global $conn;
    if ($conn) {
        $conn->close();
    }
}
 
/**
 * Fungsi untuk sanitasi input
 */
function sanitize($data) {
    global $conn;
    $data = trim($data);
    $data = stripslashes($data);
    $data = htmlspecialchars($data);
    return $conn->real_escape_string($data);
}
 
// Set timezone
date_default_timezone_set('Asia/Jakarta');
?>

c. Buat File Test Koneksi

File: test_connection.php

<?php
require_once 'config/database.php';
?>
<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Test Koneksi Database</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container mt-5">
        <div class="card">
            <div class="card-header bg-primary text-white">
                <h4 class="mb-0">Test Koneksi Database</h4>
            </div>
            <div class="card-body">
                <?php
                // Cek koneksi
                if ($conn) {
                    echo '<div class="alert alert-success">';
                    echo '<h5><i class="bi bi-check-circle"></i> Koneksi Berhasil!</h5>';
                    echo '<p><strong>Server:</strong> ' . DB_SERVER . '</p>';
                    echo '<p><strong>Database:</strong> ' . DB_NAME . '</p>';
                    echo '<p><strong>Charset:</strong> ' . $conn->character_set_name() . '</p>';
                    echo '</div>';
                    
                    // Test query
                    $result = $conn->query("SELECT COUNT(*) as total FROM buku");
                    if ($result) {
                        $row = $result->fetch_assoc();
                        echo '<div class="alert alert-info">';
                        echo '<p><strong>Total Buku di Database:</strong> ' . $row['total'] . '</p>';
                        echo '</div>';
                    }
                } else {
                    echo '<div class="alert alert-danger">';
                    echo '<h5>Koneksi Gagal</h5>';
                    echo '</div>';
                }
                
                closeConnection();
                ?>
            </div>
        </div>
    </div>
</body>
</html>

Testing:

  1. Akses: http://localhost/perpustakaan/test_connection.php
  2. Pastikan muncul "Koneksi Berhasil"
  3. Pastikan total buku ditampilkan

d. Buat Template Header & Footer

File: includes/header.php

<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><?php echo isset($page_title) ? $page_title : 'Perpustakaan'; ?></title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
    <style>
        body {
            min-height: 100vh;
            display: flex;
            flex-direction: column;
        }
        main {
            flex: 1;
        }
    </style>
</head>
<body>
    <nav class="navbar navbar-expand-lg navbar-dark bg-primary">
        <div class="container-fluid">
            <a class="navbar-brand" href="/perpustakaan/">
                <i class="bi bi-book"></i> Sistem Perpustakaan
            </a>
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="navbarNav">
                <ul class="navbar-nav">
                    <li class="nav-item">
                        <a class="nav-link" href="/perpustakaan/">
                            <i class="bi bi-house"></i> Home
                        </a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="/perpustakaan/modules/buku/index.php">
                            <i class="bi bi-book"></i> Data Buku
                        </a>
                    </li>
                </ul>
            </div>
        </div>
    </nav>
    
    <main class="py-4">

File: includes/footer.php

    </main>
    
    <footer class="bg-dark text-white text-center py-3 mt-auto">
        <p class="mb-0">&copy; <?php echo date('Y'); ?> Sistem Perpustakaan. All rights reserved.</p>
    </footer>
    
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

3. PRAKTIKUM 2: READ - Menampilkan List Buku

Tujuan

Membuat halaman yang menampilkan semua data buku dari database.

Langkah-langkah

File: modules/buku/index.php

<?php
$page_title = "Data Buku";
require_once '../../config/database.php';
require_once '../../includes/header.php';
 
// Query semua buku
$query = "SELECT * FROM buku ORDER BY created_at DESC";
$result = $conn->query($query);
?>
 
<div class="container">
    <div class="row mb-3">
        <div class="col-md-6">
            <h2><i class="bi bi-book"></i> Data Buku Perpustakaan</h2>
        </div>
        <div class="col-md-6 text-end">
            <a href="create.php" class="btn btn-primary">
                <i class="bi bi-plus-circle"></i> Tambah Buku Baru
            </a>
        </div>
    </div>
    
    <?php
    // Tampilkan pesan success/error
    if (isset($_GET['success'])) {
        echo '<div class="alert alert-success alert-dismissible fade show">';
        echo '<i class="bi bi-check-circle"></i> ' . htmlspecialchars($_GET['success']);
        echo '<button type="button" class="btn-close" data-bs-dismiss="alert"></button>';
        echo '</div>';
    }
    
    if (isset($_GET['error'])) {
        echo '<div class="alert alert-danger alert-dismissible fade show">';
        echo '<i class="bi bi-x-circle"></i> ' . htmlspecialchars($_GET['error']);
        echo '<button type="button" class="btn-close" data-bs-dismiss="alert"></button>';
        echo '</div>';
    }
    ?>
    
    <div class="card">
        <div class="card-header bg-primary text-white">
            <h5 class="mb-0">Daftar Buku</h5>
        </div>
        <div class="card-body">
            <?php if ($result->num_rows > 0): ?>
            <div class="table-responsive">
                <table class="table table-hover">
                    <thead class="table-light">
                        <tr>
                            <th width="50">No</th>
                            <th width="100">Kode</th>
                            <th>Judul Buku</th>
                            <th>Kategori</th>
                            <th>Pengarang</th>
                            <th>Penerbit</th>
                            <th width="80">Tahun</th>
                            <th width="120">Harga</th>
                            <th width="60">Stok</th>
                            <th width="150">Aksi</th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php 
                        $no = 1;
                        while ($row = $result->fetch_assoc()): 
                        ?>
                        <tr>
                            <td><?php echo $no++; ?></td>
                            <td><code><?php echo htmlspecialchars($row['kode_buku']); ?></code></td>
                            <td><?php echo htmlspecialchars($row['judul']); ?></td>
                            <td>
                                <span class="badge bg-primary">
                                    <?php echo htmlspecialchars($row['kategori']); ?>
                                </span>
                            </td>
                            <td><?php echo htmlspecialchars($row['pengarang']); ?></td>
                            <td><?php echo htmlspecialchars($row['penerbit']); ?></td>
                            <td><?php echo $row['tahun_terbit']; ?></td>
                            <td>Rp <?php echo number_format($row['harga'], 0, ',', '.'); ?></td>
                            <td class="text-center">
                                <?php if ($row['stok'] > 0): ?>
                                    <span class="badge bg-success"><?php echo $row['stok']; ?></span>
                                <?php else: ?>
                                    <span class="badge bg-danger">Habis</span>
                                <?php endif; ?>
                            </td>
                            <td>
                                <a href="edit.php?id=<?php echo $row['id_buku']; ?>" 
                                   class="btn btn-sm btn-warning">
                                    <i class="bi bi-pencil"></i>
                                </a>
                                <a href="delete.php?id=<?php echo $row['id_buku']; ?>" 
                                   class="btn btn-sm btn-danger"
                                   onclick="return confirm('Yakin ingin menghapus buku ini?')">
                                    <i class="bi bi-trash"></i>
                                </a>
                            </td>
                        </tr>
                        <?php endwhile; ?>
                    </tbody>
                </table>
            </div>
            
            <div class="alert alert-info mt-3 mb-0">
                <i class="bi bi-info-circle"></i> 
                <strong>Total:</strong> <?php echo $result->num_rows; ?> buku terdaftar
            </div>
            
            <?php else: ?>
            <div class="alert alert-warning mb-0">
                <i class="bi bi-exclamation-triangle"></i> 
                Belum ada data buku. Silakan tambah buku baru.
            </div>
            <?php endif; ?>
        </div>
    </div>
</div>
 
<?php
closeConnection();
require_once '../../includes/footer.php';
?>

Testing:

  1. Akses: http://localhost/perpustakaan/modules/buku/index.php
  2. Pastikan semua data buku dari database tampil
  3. Lihat format tabel yang rapi

4. PRAKTIKUM 3: CREATE - Tambah Buku Baru

Tujuan

Membuat form dan proses untuk menambah buku baru ke database.

Langkah-langkah

File: modules/buku/create.php

<?php
$page_title = "Tambah Buku Baru";
require_once '../../config/database.php';
require_once '../../includes/header.php';
 
// Inisialisasi variabel
$errors = [];
$kode_buku = '';
$judul = '';
$kategori = '';
$pengarang = '';
$penerbit = '';
$tahun = '';
$isbn = '';
$harga = '';
$stok = '';
$deskripsi = '';
 
// Proses form jika di-submit
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    // Ambil dan sanitasi data
    $kode_buku = sanitize($_POST['kode_buku']);
    $judul = sanitize($_POST['judul']);
    $kategori = sanitize($_POST['kategori']);
    $pengarang = sanitize($_POST['pengarang']);
    $penerbit = sanitize($_POST['penerbit']);
    $tahun = (int)$_POST['tahun'];
    $isbn = sanitize($_POST['isbn']);
    $harga = (float)$_POST['harga'];
    $stok = (int)$_POST['stok'];
    $deskripsi = sanitize($_POST['deskripsi']);
    
    // Validasi
    if (empty($kode_buku)) {
        $errors[] = "Kode buku wajib diisi";
    }
    
    if (empty($judul)) {
        $errors[] = "Judul buku wajib diisi";
    } elseif (strlen($judul) < 3) {
        $errors[] = "Judul minimal 3 karakter";
    }
    
    if (empty($kategori)) {
        $errors[] = "Kategori wajib dipilih";
    }
    
    if (empty($pengarang)) {
        $errors[] = "Pengarang wajib diisi";
    }
    
    if (empty($penerbit)) {
        $errors[] = "Penerbit wajib diisi";
    }
    
    if (empty($tahun) || $tahun < 1900 || $tahun > date('Y')) {
        $errors[] = "Tahun terbit tidak valid";
    }
    
    if ($harga < 0) {
        $errors[] = "Harga tidak boleh negatif";
    }
    
    if ($stok < 0) {
        $errors[] = "Stok tidak boleh negatif";
    }
    
    // Cek kode buku duplikat
    if (empty($errors)) {
        $stmt = $conn->prepare("SELECT id_buku FROM buku WHERE kode_buku = ?");
        $stmt->bind_param("s", $kode_buku);
        $stmt->execute();
        $result = $stmt->get_result();
        
        if ($result->num_rows > 0) {
            $errors[] = "Kode buku sudah digunakan";
        }
        $stmt->close();
    }
    
    // Jika tidak ada error, insert ke database
    if (count($errors) == 0) {
        $stmt = $conn->prepare("INSERT INTO buku (kode_buku, judul, kategori, pengarang, penerbit, tahun_terbit, isbn, harga, stok, deskripsi) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
        
        $stmt->bind_param("sssssissis", 
            $kode_buku,
            $judul,
            $kategori,
            $pengarang,
            $penerbit,
            $tahun,
            $isbn,
            $harga,
            $stok,
            $deskripsi
        );
        
        if ($stmt->execute()) {
            $stmt->close();
            closeConnection();
            header("Location: index.php?success=" . urlencode("Buku '$judul' berhasil ditambahkan"));
            exit();
        } else {
            $errors[] = "Error database: " . $stmt->error;
        }
        
        $stmt->close();
    }
}
?>
 
<div class="container">
    <div class="row">
        <div class="col-md-8 offset-md-2">
            <div class="card">
                <div class="card-header bg-primary text-white">
                    <h4 class="mb-0">
                        <i class="bi bi-plus-circle"></i> Tambah Buku Baru
                    </h4>
                </div>
                <div class="card-body">
                    <!-- Tampilkan Error -->
                    <?php if (count($errors) > 0): ?>
                    <div class="alert alert-danger">
                        <h6><i class="bi bi-exclamation-triangle"></i> Terdapat kesalahan:</h6>
                        <ul class="mb-0">
                            <?php foreach ($errors as $error): ?>
                                <li><?php echo $error; ?></li>
                            <?php endforeach; ?>
                        </ul>
                    </div>
                    <?php endif; ?>
                    
                    <form method="POST" action="">
                        <!-- Kode Buku & Judul -->
                        <div class="row">
                            <div class="col-md-4 mb-3">
                                <label for="kode_buku" class="form-label">
                                    Kode Buku <span class="text-danger">*</span>
                                </label>
                                <input type="text" 
                                       class="form-control" 
                                       id="kode_buku" 
                                       name="kode_buku" 
                                       value="<?php echo htmlspecialchars($kode_buku); ?>"
                                       placeholder="BK-001" 
                                       required>
                            </div>
                            
                            <div class="col-md-8 mb-3">
                                <label for="judul" class="form-label">
                                    Judul Buku <span class="text-danger">*</span>
                                </label>
                                <input type="text" 
                                       class="form-control" 
                                       id="judul" 
                                       name="judul" 
                                       value="<?php echo htmlspecialchars($judul); ?>"
                                       placeholder="Masukkan judul buku" 
                                       required>
                            </div>
                        </div>
                        
                        <!-- Kategori -->
                        <div class="mb-3">
                            <label for="kategori" class="form-label">
                                Kategori <span class="text-danger">*</span>
                            </label>
                            <select class="form-select" id="kategori" name="kategori" required>
                                <option value="">-- Pilih Kategori --</option>
                                <option value="Programming" <?php echo ($kategori == 'Programming') ? 'selected' : ''; ?>>Programming</option>
                                <option value="Database" <?php echo ($kategori == 'Database') ? 'selected' : ''; ?>>Database</option>
                                <option value="Web Design" <?php echo ($kategori == 'Web Design') ? 'selected' : ''; ?>>Web Design</option>
                                <option value="Networking" <?php echo ($kategori == 'Networking') ? 'selected' : ''; ?>>Networking</option>
                            </select>
                        </div>
                        
                        <!-- Pengarang & Penerbit -->
                        <div class="row">
                            <div class="col-md-6 mb-3">
                                <label for="pengarang" class="form-label">
                                    Pengarang <span class="text-danger">*</span>
                                </label>
                                <input type="text" 
                                       class="form-control" 
                                       id="pengarang" 
                                       name="pengarang" 
                                       value="<?php echo htmlspecialchars($pengarang); ?>"
                                       placeholder="Nama pengarang" 
                                       required>
                            </div>
                            
                            <div class="col-md-6 mb-3">
                                <label for="penerbit" class="form-label">
                                    Penerbit <span class="text-danger">*</span>
                                </label>
                                <input type="text" 
                                       class="form-control" 
                                       id="penerbit" 
                                       name="penerbit" 
                                       value="<?php echo htmlspecialchars($penerbit); ?>"
                                       placeholder="Nama penerbit" 
                                       required>
                            </div>
                        </div>
                        
                        <!-- Tahun & ISBN -->
                        <div class="row">
                            <div class="col-md-6 mb-3">
                                <label for="tahun" class="form-label">
                                    Tahun Terbit <span class="text-danger">*</span>
                                </label>
                                <input type="number" 
                                       class="form-control" 
                                       id="tahun" 
                                       name="tahun" 
                                       value="<?php echo htmlspecialchars($tahun); ?>"
                                       min="1900" 
                                       max="<?php echo date('Y'); ?>" 
                                       placeholder="<?php echo date('Y'); ?>" 
                                       required>
                            </div>
                            
                            <div class="col-md-6 mb-3">
                                <label for="isbn" class="form-label">ISBN</label>
                                <input type="text" 
                                       class="form-control" 
                                       id="isbn" 
                                       name="isbn" 
                                       value="<?php echo htmlspecialchars($isbn); ?>"
                                       placeholder="978-602-1234-56-7">
                            </div>
                        </div>
                        
                        <!-- Harga & Stok -->
                        <div class="row">
                            <div class="col-md-6 mb-3">
                                <label for="harga" class="form-label">
                                    Harga (Rp) <span class="text-danger">*</span>
                                </label>
                                <input type="number" 
                                       class="form-control" 
                                       id="harga" 
                                       name="harga" 
                                       value="<?php echo htmlspecialchars($harga); ?>"
                                       min="0" 
                                       step="1000" 
                                       placeholder="75000" 
                                       required>
                            </div>
                            
                            <div class="col-md-6 mb-3">
                                <label for="stok" class="form-label">
                                    Stok <span class="text-danger">*</span>
                                </label>
                                <input type="number" 
                                       class="form-control" 
                                       id="stok" 
                                       name="stok" 
                                       value="<?php echo htmlspecialchars($stok); ?>"
                                       min="0" 
                                       placeholder="10" 
                                       required>
                            </div>
                        </div>
                        
                        <!-- Deskripsi -->
                        <div class="mb-3">
                            <label for="deskripsi" class="form-label">Deskripsi</label>
                            <textarea class="form-control" 
                                      id="deskripsi" 
                                      name="deskripsi" 
                                      rows="3" 
                                      placeholder="Deskripsi singkat tentang buku..."><?php echo htmlspecialchars($deskripsi); ?></textarea>
                        </div>
                        
                        <hr>
                        
                        <div class="d-grid gap-2">
                            <button type="submit" class="btn btn-primary">
                                <i class="bi bi-save"></i> Simpan Data Buku
                            </button>
                            <a href="index.php" class="btn btn-secondary">
                                <i class="bi bi-x-circle"></i> Batal
                            </a>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
 
<?php
closeConnection();
require_once '../../includes/footer.php';
?>

Testing:

  1. Akses: http://localhost/perpustakaan/modules/buku/create.php
  2. Coba submit tanpa mengisi (lihat validasi required)
  3. Isi form dengan data valid dan submit
  4. Pastikan redirect ke index.php dengan pesan sukses
  5. Cek di database apakah data tersimpan

5. PRAKTIKUM 4: UPDATE - Edit Data Buku

Tujuan

Membuat halaman untuk mengedit data buku yang sudah ada.

Langkah-langkah

File: modules/buku/edit.php

<?php
$page_title = "Edit Data Buku";
require_once '../../config/database.php';
require_once '../../includes/header.php';
 
// Cek apakah ada ID di URL
if (!isset($_GET['id']) || empty($_GET['id'])) {
    header("Location: index.php?error=ID buku tidak valid");
    exit();
}
 
$id_buku = (int)$_GET['id'];
$errors = [];
 
// Ambil data buku untuk ditampilkan di form
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
    $stmt = $conn->prepare("SELECT * FROM buku WHERE id_buku = ?");
    $stmt->bind_param("i", $id_buku);
    $stmt->execute();
    $result = $stmt->get_result();
    
    if ($result->num_rows == 0) {
        $stmt->close();
        closeConnection();
        header("Location: index.php?error=Buku tidak ditemukan");
        exit();
    }
    
    $buku = $result->fetch_assoc();
    $stmt->close();
    
    // Set variabel untuk form
    $kode_buku = $buku['kode_buku'];
    $judul = $buku['judul'];
    $kategori = $buku['kategori'];
    $pengarang = $buku['pengarang'];
    $penerbit = $buku['penerbit'];
    $tahun = $buku['tahun_terbit'];
    $isbn = $buku['isbn'];
    $harga = $buku['harga'];
    $stok = $buku['stok'];
    $deskripsi = $buku['deskripsi'];
}
 
// Proses update jika form di-submit
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    // Ambil data dari form
    $kode_buku = sanitize($_POST['kode_buku']);
    $judul = sanitize($_POST['judul']);
    $kategori = sanitize($_POST['kategori']);
    $pengarang = sanitize($_POST['pengarang']);
    $penerbit = sanitize($_POST['penerbit']);
    $tahun = (int)$_POST['tahun'];
    $isbn = sanitize($_POST['isbn']);
    $harga = (float)$_POST['harga'];
    $stok = (int)$_POST['stok'];
    $deskripsi = sanitize($_POST['deskripsi']);
    
    // Validasi (sama seperti create)
    if (empty($kode_buku)) {
        $errors[] = "Kode buku wajib diisi";
    }
    
    if (empty($judul)) {
        $errors[] = "Judul buku wajib diisi";
    } elseif (strlen($judul) < 3) {
        $errors[] = "Judul minimal 3 karakter";
    }
    
    if (empty($kategori)) {
        $errors[] = "Kategori wajib dipilih";
    }
    
    if (empty($pengarang)) {
        $errors[] = "Pengarang wajib diisi";
    }
    
    if (empty($penerbit)) {
        $errors[] = "Penerbit wajib diisi";
    }
    
    if (empty($tahun) || $tahun < 1900 || $tahun > date('Y')) {
        $errors[] = "Tahun terbit tidak valid";
    }
    
    if ($harga < 0) {
        $errors[] = "Harga tidak boleh negatif";
    }
    
    if ($stok < 0) {
        $errors[] = "Stok tidak boleh negatif";
    }
    
    // Cek kode buku duplikat (kecuali untuk buku ini sendiri)
    if (empty($errors)) {
        $stmt = $conn->prepare("SELECT id_buku FROM buku WHERE kode_buku = ? AND id_buku != ?");
        $stmt->bind_param("si", $kode_buku, $id_buku);
        $stmt->execute();
        $result = $stmt->get_result();
        
        if ($result->num_rows > 0) {
            $errors[] = "Kode buku sudah digunakan oleh buku lain";
        }
        $stmt->close();
    }
    
    // Jika tidak ada error, update database
    if (count($errors) == 0) {
        $stmt = $conn->prepare("UPDATE buku SET kode_buku = ?, judul = ?, kategori = ?, pengarang = ?, penerbit = ?, tahun_terbit = ?, isbn = ?, harga = ?, stok = ?, deskripsi = ? WHERE id_buku = ?");
        
        $stmt->bind_param("sssssissisi", 
            $kode_buku,
            $judul,
            $kategori,
            $pengarang,
            $penerbit,
            $tahun,
            $isbn,
            $harga,
            $stok,
            $deskripsi,
            $id_buku
        );
        
        if ($stmt->execute()) {
            $stmt->close();
            closeConnection();
            header("Location: index.php?success=" . urlencode("Buku '$judul' berhasil diupdate"));
            exit();
        } else {
            $errors[] = "Error database: " . $stmt->error;
        }
        
        $stmt->close();
    }
}
?>
 
<div class="container">
    <div class="row">
        <div class="col-md-8 offset-md-2">
            <div class="card">
                <div class="card-header bg-warning">
                    <h4 class="mb-0">
                        <i class="bi bi-pencil"></i> Edit Data Buku
                    </h4>
                </div>
                <div class="card-body">
                    <!-- Tampilkan Error -->
                    <?php if (count($errors) > 0): ?>
                    <div class="alert alert-danger">
                        <h6><i class="bi bi-exclamation-triangle"></i> Terdapat kesalahan:</h6>
                        <ul class="mb-0">
                            <?php foreach ($errors as $error): ?>
                                <li><?php echo $error; ?></li>
                            <?php endforeach; ?>
                        </ul>
                    </div>
                    <?php endif; ?>
                    
                    <form method="POST" action="">
                        <!-- Form sama seperti create.php, tapi sudah terisi data -->
                        
                        <!-- Kode Buku & Judul -->
                        <div class="row">
                            <div class="col-md-4 mb-3">
                                <label for="kode_buku" class="form-label">
                                    Kode Buku <span class="text-danger">*</span>
                                </label>
                                <input type="text" 
                                       class="form-control" 
                                       id="kode_buku" 
                                       name="kode_buku" 
                                       value="<?php echo htmlspecialchars($kode_buku); ?>"
                                       required>
                            </div>
                            
                            <div class="col-md-8 mb-3">
                                <label for="judul" class="form-label">
                                    Judul Buku <span class="text-danger">*</span>
                                </label>
                                <input type="text" 
                                       class="form-control" 
                                       id="judul" 
                                       name="judul" 
                                       value="<?php echo htmlspecialchars($judul); ?>"
                                       required>
                            </div>
                        </div>
                        
                        <!-- Kategori -->
                        <div class="mb-3">
                            <label for="kategori" class="form-label">
                                Kategori <span class="text-danger">*</span>
                            </label>
                            <select class="form-select" id="kategori" name="kategori" required>
                                <option value="">-- Pilih Kategori --</option>
                                <option value="Programming" <?php echo ($kategori == 'Programming') ? 'selected' : ''; ?>>Programming</option>
                                <option value="Database" <?php echo ($kategori == 'Database') ? 'selected' : ''; ?>>Database</option>
                                <option value="Web Design" <?php echo ($kategori == 'Web Design') ? 'selected' : ''; ?>>Web Design</option>
                                <option value="Networking" <?php echo ($kategori == 'Networking') ? 'selected' : ''; ?>>Networking</option>
                            </select>
                        </div>
                        
                        <!-- Pengarang & Penerbit -->
                        <div class="row">
                            <div class="col-md-6 mb-3">
                                <label for="pengarang" class="form-label">
                                    Pengarang <span class="text-danger">*</span>
                                </label>
                                <input type="text" 
                                       class="form-control" 
                                       id="pengarang" 
                                       name="pengarang" 
                                       value="<?php echo htmlspecialchars($pengarang); ?>"
                                       required>
                            </div>
                            
                            <div class="col-md-6 mb-3">
                                <label for="penerbit" class="form-label">
                                    Penerbit <span class="text-danger">*</span>
                                </label>
                                <input type="text" 
                                       class="form-control" 
                                       id="penerbit" 
                                       name="penerbit" 
                                       value="<?php echo htmlspecialchars($penerbit); ?>"
                                       required>
                            </div>
                        </div>
                        
                        <!-- Tahun & ISBN -->
                        <div class="row">
                            <div class="col-md-6 mb-3">
                                <label for="tahun" class="form-label">
                                    Tahun Terbit <span class="text-danger">*</span>
                                </label>
                                <input type="number" 
                                       class="form-control" 
                                       id="tahun" 
                                       name="tahun" 
                                       value="<?php echo htmlspecialchars($tahun); ?>"
                                       min="1900" 
                                       max="<?php echo date('Y'); ?>" 
                                       required>
                            </div>
                            
                            <div class="col-md-6 mb-3">
                                <label for="isbn" class="form-label">ISBN</label>
                                <input type="text" 
                                       class="form-control" 
                                       id="isbn" 
                                       name="isbn" 
                                       value="<?php echo htmlspecialchars($isbn); ?>">
                            </div>
                        </div>
                        
                        <!-- Harga & Stok -->
                        <div class="row">
                            <div class="col-md-6 mb-3">
                                <label for="harga" class="form-label">
                                    Harga (Rp) <span class="text-danger">*</span>
                                </label>
                                <input type="number" 
                                       class="form-control" 
                                       id="harga" 
                                       name="harga" 
                                       value="<?php echo htmlspecialchars($harga); ?>"
                                       min="0" 
                                       step="1000" 
                                       required>
                            </div>
                            
                            <div class="col-md-6 mb-3">
                                <label for="stok" class="form-label">
                                    Stok <span class="text-danger">*</span>
                                </label>
                                <input type="number" 
                                       class="form-control" 
                                       id="stok" 
                                       name="stok" 
                                       value="<?php echo htmlspecialchars($stok); ?>"
                                       min="0" 
                                       required>
                            </div>
                        </div>
                        
                        <!-- Deskripsi -->
                        <div class="mb-3">
                            <label for="deskripsi" class="form-label">Deskripsi</label>
                            <textarea class="form-control" 
                                      id="deskripsi" 
                                      name="deskripsi" 
                                      rows="3"><?php echo htmlspecialchars($deskripsi); ?></textarea>
                        </div>
                        
                        <hr>
                        
                        <div class="d-grid gap-2">
                            <button type="submit" class="btn btn-warning">
                                <i class="bi bi-save"></i> Update Data Buku
                            </button>
                            <a href="index.php" class="btn btn-secondary">
                                <i class="bi bi-x-circle"></i> Batal
                            </a>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
 
<?php
closeConnection();
require_once '../../includes/footer.php';
?>

Testing:

  1. Dari halaman index, klik tombol Edit pada salah satu buku
  2. Pastikan form terisi dengan data buku yang dipilih
  3. Ubah beberapa field (misal: harga, stok)
  4. Submit dan pastikan data terupdate di database
  5. Cek redirect dengan success message

6. PRAKTIKUM 5: DELETE - Hapus Data Buku

Tujuan

Membuat proses untuk menghapus data buku dari database.

Langkah-langkah

File: modules/buku/delete.php

<?php
require_once '../../config/database.php';
 
// Cek apakah ada ID di URL
if (!isset($_GET['id']) || empty($_GET['id'])) {
    header("Location: index.php?error=ID buku tidak valid");
    exit();
}
 
$id_buku = (int)$_GET['id'];
 
// Ambil data buku untuk pesan konfirmasi
$stmt = $conn->prepare("SELECT judul FROM buku WHERE id_buku = ?");
$stmt->bind_param("i", $id_buku);
$stmt->execute();
$result = $stmt->get_result();
 
if ($result->num_rows == 0) {
    $stmt->close();
    closeConnection();
    header("Location: index.php?error=Buku tidak ditemukan");
    exit();
}
 
$buku = $result->fetch_assoc();
$judul_buku = $buku['judul'];
$stmt->close();
 
// Proses delete
$stmt = $conn->prepare("DELETE FROM buku WHERE id_buku = ?");
$stmt->bind_param("i", $id_buku);
 
if ($stmt->execute()) {
    if ($stmt->affected_rows > 0) {
        $stmt->close();
        closeConnection();
        header("Location: index.php?success=" . urlencode("Buku '$judul_buku' berhasil dihapus"));
        exit();
    } else {
        $stmt->close();
        closeConnection();
        header("Location: index.php?error=Gagal menghapus data");
        exit();
    }
} else {
    $error = $stmt->error;
    $stmt->close();
    closeConnection();
    header("Location: index.php?error=" . urlencode("Error database: $error"));
    exit();
}
?>

Testing:

  1. Dari halaman index, klik tombol Hapus (ikon trash) pada salah satu buku
  2. Konfirmasi JavaScript popup "Yakin ingin menghapus?"
  3. Klik OK dan pastikan redirect dengan pesan sukses
  4. Cek database - buku harus sudah terhapus
  5. Coba akses delete.php?id=999 (ID tidak ada) - harus muncul error

7. PRAKTIKUM 6: Enhancement - Search & Pagination

Tujuan

Menambahkan fitur pencarian dan pagination pada list buku.

Langkah-langkah

Modifikasi file modules/buku/index.php:

Ganti bagian query dengan code berikut:

<?php
$page_title = "Data Buku";
require_once '../../config/database.php';
require_once '../../includes/header.php';
 
// Pagination
$limit = 10; // Jumlah data per halaman
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$offset = ($page - 1) * $limit;
 
// Search
$search = isset($_GET['search']) ? sanitize($_GET['search']) : '';
 
// Build query
if (!empty($search)) {
    // Query dengan search
    $query = "SELECT * FROM buku 
              WHERE judul LIKE ? OR pengarang LIKE ? OR kategori LIKE ?
              ORDER BY created_at DESC 
              LIMIT ? OFFSET ?";
    
    $search_param = "%$search%";
    $stmt = $conn->prepare($query);
    $stmt->bind_param("sssii", $search_param, $search_param, $search_param, $limit, $offset);
    $stmt->execute();
    $result = $stmt->get_result();
    
    // Count total untuk pagination
    $count_query = "SELECT COUNT(*) as total FROM buku 
                    WHERE judul LIKE ? OR pengarang LIKE ? OR kategori LIKE ?";
    $stmt_count = $conn->prepare($count_query);
    $stmt_count->bind_param("sss", $search_param, $search_param, $search_param);
    $stmt_count->execute();
    $total_rows = $stmt_count->get_result()->fetch_assoc()['total'];
    
} else {
    // Query tanpa search
    $query = "SELECT * FROM buku ORDER BY created_at DESC LIMIT ? OFFSET ?";
    $stmt = $conn->prepare($query);
    $stmt->bind_param("ii", $limit, $offset);
    $stmt->execute();
    $result = $stmt->get_result();
    
    // Count total
    $total_rows = $conn->query("SELECT COUNT(*) as total FROM buku")->fetch_assoc()['total'];
}
 
// Hitung total halaman
$total_pages = ceil($total_rows / $limit);
?>
 
<div class="container">
    <div class="row mb-3">
        <div class="col-md-6">
            <h2><i class="bi bi-book"></i> Data Buku Perpustakaan</h2>
        </div>
        <div class="col-md-6 text-end">
            <a href="create.php" class="btn btn-primary">
                <i class="bi bi-plus-circle"></i> Tambah Buku Baru
            </a>
        </div>
    </div>
    
    <?php
    // Success/Error messages (sama seperti sebelumnya)
    if (isset($_GET['success'])) {
        echo '<div class="alert alert-success alert-dismissible fade show">';
        echo '<i class="bi bi-check-circle"></i> ' . htmlspecialchars($_GET['success']);
        echo '<button type="button" class="btn-close" data-bs-dismiss="alert"></button>';
        echo '</div>';
    }
    
    if (isset($_GET['error'])) {
        echo '<div class="alert alert-danger alert-dismissible fade show">';
        echo '<i class="bi bi-x-circle"></i> ' . htmlspecialchars($_GET['error']);
        echo '<button type="button" class="btn-close" data-bs-dismiss="alert"></button>';
        echo '</div>';
    }
    ?>
    
    <!-- Search Box -->
    <div class="card mb-3">
        <div class="card-body">
            <form method="GET" action="">
                <div class="input-group">
                    <input type="text" 
                           class="form-control" 
                           name="search" 
                           value="<?php echo htmlspecialchars($search); ?>"
                           placeholder="Cari judul, pengarang, atau kategori...">
                    <button class="btn btn-primary" type="submit">
                        <i class="bi bi-search"></i> Cari
                    </button>
                    <?php if (!empty($search)): ?>
                    <a href="index.php" class="btn btn-secondary">
                        <i class="bi bi-x-circle"></i> Reset
                    </a>
                    <?php endif; ?>
                </div>
            </form>
        </div>
    </div>
    
    <div class="card">
        <div class="card-header bg-primary text-white">
            <h5 class="mb-0">
                Daftar Buku
                <?php if (!empty($search)): ?>
                    <span class="badge bg-light text-dark">
                        Hasil pencarian: "<?php echo htmlspecialchars($search); ?>"
                    </span>
                <?php endif; ?>
            </h5>
        </div>
        <div class="card-body">
            <?php if ($result->num_rows > 0): ?>
            <div class="table-responsive">
                <table class="table table-hover">
                    <thead class="table-light">
                        <tr>
                            <th width="50">No</th>
                            <th width="100">Kode</th>
                            <th>Judul Buku</th>
                            <th>Kategori</th>
                            <th>Pengarang</th>
                            <th>Penerbit</th>
                            <th width="80">Tahun</th>
                            <th width="120">Harga</th>
                            <th width="60">Stok</th>
                            <th width="150">Aksi</th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php 
                        $no = $offset + 1;
                        while ($row = $result->fetch_assoc()): 
                        ?>
                        <tr>
                            <td><?php echo $no++; ?></td>
                            <td><code><?php echo htmlspecialchars($row['kode_buku']); ?></code></td>
                            <td><?php echo htmlspecialchars($row['judul']); ?></td>
                            <td>
                                <span class="badge bg-primary">
                                    <?php echo htmlspecialchars($row['kategori']); ?>
                                </span>
                            </td>
                            <td><?php echo htmlspecialchars($row['pengarang']); ?></td>
                            <td><?php echo htmlspecialchars($row['penerbit']); ?></td>
                            <td><?php echo $row['tahun_terbit']; ?></td>
                            <td>Rp <?php echo number_format($row['harga'], 0, ',', '.'); ?></td>
                            <td class="text-center">
                                <?php if ($row['stok'] > 0): ?>
                                    <span class="badge bg-success"><?php echo $row['stok']; ?></span>
                                <?php else: ?>
                                    <span class="badge bg-danger">Habis</span>
                                <?php endif; ?>
                            </td>
                            <td>
                                <a href="edit.php?id=<?php echo $row['id_buku']; ?>" 
                                   class="btn btn-sm btn-warning">
                                    <i class="bi bi-pencil"></i>
                                </a>
                                <a href="delete.php?id=<?php echo $row['id_buku']; ?>" 
                                   class="btn btn-sm btn-danger"
                                   onclick="return confirm('Yakin ingin menghapus buku ini?')">
                                    <i class="bi bi-trash"></i>
                                </a>
                            </td>
                        </tr>
                        <?php endwhile; ?>
                    </tbody>
                </table>
            </div>
            
            <!-- Pagination -->
            <?php if ($total_pages > 1): ?>
            <nav aria-label="Page navigation" class="mt-3">
                <ul class="pagination justify-content-center">
                    <!-- Previous -->
                    <li class="page-item <?php echo ($page <= 1) ? 'disabled' : ''; ?>">
                        <a class="page-link" href="?page=<?php echo ($page - 1); ?><?php echo !empty($search) ? '&search=' . urlencode($search) : ''; ?>">
                            Previous
                        </a>
                    </li>
                    
                    <!-- Page Numbers -->
                    <?php for ($i = 1; $i <= $total_pages; $i++): ?>
                    <li class="page-item <?php echo ($page == $i) ? 'active' : ''; ?>">
                        <a class="page-link" href="?page=<?php echo $i; ?><?php echo !empty($search) ? '&search=' . urlencode($search) : ''; ?>">
                            <?php echo $i; ?>
                        </a>
                    </li>
                    <?php endfor; ?>
                    
                    <!-- Next -->
                    <li class="page-item <?php echo ($page >= $total_pages) ? 'disabled' : ''; ?>">
                        <a class="page-link" href="?page=<?php echo ($page + 1); ?><?php echo !empty($search) ? '&search=' . urlencode($search) : ''; ?>">
                            Next
                        </a>
                    </li>
                </ul>
            </nav>
            <?php endif; ?>
            
            <div class="alert alert-info mt-3 mb-0">
                <i class="bi bi-info-circle"></i> 
                <strong>Total:</strong> <?php echo $total_rows; ?> buku terdaftar
                <?php if (!empty($search)): ?>
                    | <strong>Ditemukan:</strong> <?php echo $result->num_rows; ?> buku
                <?php endif; ?>
                | <strong>Halaman:</strong> <?php echo $page; ?> dari <?php echo $total_pages; ?>
            </div>
            
            <?php else: ?>
            <div class="alert alert-warning mb-0">
                <i class="bi bi-exclamation-triangle"></i> 
                <?php if (!empty($search)): ?>
                    Tidak ada buku yang cocok dengan pencarian "<?php echo htmlspecialchars($search); ?>"
                <?php else: ?>
                    Belum ada data buku. Silakan tambah buku baru.
                <?php endif; ?>
            </div>
            <?php endif; ?>
        </div>
    </div>
</div>
 
<?php
if (isset($stmt)) $stmt->close();
if (isset($stmt_count)) $stmt_count->close();
closeConnection();
require_once '../../includes/footer.php';
?>

Testing:

  1. Akses halaman index
  2. Coba search dengan keyword (misal: "PHP")
  3. Lihat pagination di bawah tabel
  4. Klik halaman 2, 3, dst (jika ada)
  5. Search + pagination harus bekerja bersama

E. TUGAS

Tugas: Mini Project CRUD Lengkap (100%)

Instruksi: Buat sistem CRUD lengkap untuk Tabel Anggota Perpustakaan dengan spesifikasi berikut.

Spesifikasi Tabel Anggota:

CREATE TABLE anggota (
    id_anggota INT AUTO_INCREMENT PRIMARY KEY,
    kode_anggota VARCHAR(20) UNIQUE NOT NULL,
    nama VARCHAR(100) NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL,
    telepon VARCHAR(15) NOT NULL,
    alamat TEXT NOT NULL,
    tanggal_lahir DATE NOT NULL,
    jenis_kelamin ENUM('Laki-laki', 'Perempuan') NOT NULL,
    pekerjaan VARCHAR(50),
    tanggal_daftar DATE NOT NULL,
    status ENUM('Aktif', 'Nonaktif') DEFAULT 'Aktif',
    foto VARCHAR(255),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

Fitur yang Harus Dibuat:

  1. READ (30%)

    • Halaman list anggota dalam tabel
    • Pagination (10 data per halaman)
    • Search by nama/email/telepon
    • Badge status (Aktif/Nonaktif) dengan warna berbeda
    • Badge jenis kelamin
    • Tampilkan foto anggota (jika ada)
  2. CREATE (25%)

    • Form tambah anggota baru
    • Validasi semua field required
    • Validasi email format
    • Validasi telepon (08xxxxxxxxxx)
    • Validasi umur minimal 10 tahun
    • Default status: Aktif
    • Default tanggal daftar: hari ini
    • Upload foto (optional)
  3. UPDATE (25%)

    • Form edit anggota
    • Populate form dengan data yang ada
    • Validasi sama seperti create
    • Validasi email/kode unik (kecuali untuk data sendiri)
    • Update foto baru (optional, jika tidak diubah pakai foto lama)
  4. DELETE (10%)

    • Proses hapus anggota
    • Konfirmasi sebelum hapus
    • Hapus foto juga (jika ada)
    • Redirect dengan pesan sukses
  5. Bonus (10%)

    • Export data ke Excel
    • Filter by status (Aktif/Nonaktif)
    • Filter by jenis kelamin
    • Statistik dashboard (total anggota, aktif, nonaktif, dll)

Struktur Folder:

perpustakaan/
└── modules/
    └── anggota/
        ├── index.php    # List + search + pagination
        ├── create.php   # Form tambah
        ├── edit.php     # Form edit
        ├── delete.php   # Proses hapus
        └── uploads/     # Folder foto anggota

Submission:

  • Format: Link repository GitHub (berisi folder modules/anggota/ dan SQL dump tabel)
  • Deadline: 1 minggu sebelum UTS
  • Upload ke: Ngaji UIN Gusdur (submit link repository GitHub)

F. EVALUASI

1. Checklist Pre-UTS

Pastikan mahasiswa sudah bisa:

NoKompetensiStatus
1Membuat koneksi database dengan mysqli☐
2Menggunakan prepared statements☐
3INSERT data ke database☐
4SELECT data dari database☐
5UPDATE data di database☐
6DELETE data dari database☐
7Validasi input form☐
8Sanitasi data☐
9Error handling database☐
10Membuat CRUD lengkap☐

Target: Semua checklist harus ✓ sebelum UTS


2. Soal Latihan Pre-UTS

Soal 1: Teori (20 poin)

  1. Jelaskan perbedaan mysqli procedural dan OOP! (5 poin)
  2. Apa itu prepared statement dan mengapa penting? (5 poin)
  3. Jelaskan cara kerja 3 langkah prepared statement! (5 poin)
  4. Sebutkan 4 operasi dasar CRUD! (5 poin)

Soal 2: Praktik (80 poin)

Buat mini project CRUD untuk tabel Kategori Buku dengan spesifikasi:

Tabel:

CREATE TABLE kategori (
    id_kategori INT AUTO_INCREMENT PRIMARY KEY,
    kode_kategori VARCHAR(10) UNIQUE NOT NULL,
    nama_kategori VARCHAR(50) NOT NULL,
    deskripsi TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Fitur:

  • List kategori (20 poin)
  • Tambah kategori baru (20 poin)
  • Edit kategori (20 poin)
  • Hapus kategori (20 poin)

Waktu: 90 menit


G. REFERENSI

1. Dokumentasi Resmi

2. Tutorial Online

3. Security Resources


H. CATATAN PENTING

Untuk Mahasiswa:

1. Security Best Practices

✅ DO:

// Gunakan prepared statements
$stmt = $conn->prepare("SELECT * FROM buku WHERE id = ?");
$stmt->bind_param("i", $id);
 
// Sanitasi output
echo htmlspecialchars($data);
 
// Validasi input
if (empty($input) || !is_numeric($input)) {
    // Handle error
}

❌ DON'T:

// Direct query - SQL Injection vulnerable!
$query = "SELECT * FROM buku WHERE id = $id";
 
// No sanitization - XSS vulnerable!
echo $_POST['data'];
 
// No validation
$conn->query("INSERT ... VALUES ('$_POST[name]')");

2. Common Mistakes

❌ Error 1: Lupa close statement

// SALAH
$stmt = $conn->prepare("...");
$stmt->execute();
// Lupa $stmt->close()

✅ Benar:

$stmt = $conn->prepare("...");
$stmt->execute();
$stmt->close(); // WAJIB!

❌ Error 2: Salah bind_param type

// SALAH - harga adalah decimal/double
$stmt->bind_param("i", $harga); // i = integer
 
// BENAR
$stmt->bind_param("d", $harga); // d = double

❌ Error 3: Tidak cek num_rows

// SALAH
$result = $stmt->get_result();
$data = $result->fetch_assoc(); // Bisa NULL!
 
// BENAR
if ($result->num_rows > 0) {
    $data = $result->fetch_assoc();
} else {
    // Handle tidak ada data
}

3. Testing Checklist

Setiap fitur harus ditest:

CREATE:

  • ✓ Submit dengan data valid
  • ✓ Submit dengan field kosong
  • ✓ Submit dengan data duplikat (kode/email)
  • ✓ Submit dengan data invalid (harga negatif, dll)

READ:

  • ✓ Tampilkan semua data
  • ✓ Tampilan jika data kosong
  • ✓ Search dengan keyword valid
  • ✓ Search tanpa hasil
  • ✓ Pagination halaman 1, 2, terakhir

UPDATE:

  • ✓ Edit data yang ada
  • ✓ Submit dengan data valid
  • ✓ Submit dengan field kosong
  • ✓ Submit dengan kode/email duplikat (dari buku lain)

DELETE:

  • ✓ Hapus data yang ada
  • ✓ Coba hapus ID yang tidak ada
  • ✓ Konfirmasi popup bekerja

I. PERSIAPAN UTS

Materi yang Akan Diujikan

Teori (30%):

  • Konsep CRUD
  • SQL DDL & DML
  • Prepared statements
  • Security (SQL injection, XSS)

Praktik (70%):

  • Membuat CRUD lengkap untuk satu tabel
  • Validasi input
  • Error handling
  • UI dengan Bootstrap

Yang Perlu Dipelajari

  1. Review semua praktikum pertemuan 2-7
  2. Latihan membuat CRUD dari nol
  3. Pahami prepared statements
  4. Kuasai validasi form
  5. Bisa debugging error database

Tips Menghadapi UTS

  1. Persiapan:

    • Install XAMPP/Laragon di laptop sendiri
    • Backup database perpustakaan
    • Simpan template code (koneksi, header, footer)
  2. Saat Ujian:

    • Baca soal dengan teliti
    • Buat database dan tabel dulu
    • Test setiap fitur sebelum submit
    • Perhatikan validasi dan error handling
  3. Time Management:

    • Database setup: 10 menit
    • CREATE: 20 menit
    • READ: 15 menit
    • UPDATE: 20 menit
    • DELETE: 10 menit
    • Testing: 15 menit

Selamat Belajar! 🚀📚

"Any fool can write code that a computer can understand. Good programmers write code that humans can understand."

  • Martin Fowler

End of Module - Pertemuan 7

Next: Pertemuan 8 - UTS (Ujian Tengah Semester)