Membuat CRUD Sederhana PHP Bootstrap: Tutorial Lengkap untuk Pemula

By | August 16, 2025

Membuat CRUD Sederhana PHP Bootstrap: Tutorial Lengkap untuk Pemula

Dalam dunia web development, membuat CRUD sederhana PHP Bootstrap adalah skill fundamental yang harus dikuasai setiap developer. CRUD (Create, Read, Update, Delete) merupakan operasi dasar dalam pengelolaan data yang akan Anda temukan di hampir setiap aplikasi web modern.

Tutorial ini akan memandu Anda step-by-step untuk membangun aplikasi CRUD yang responsive dan user-friendly menggunakan PHP sebagai backend dan Bootstrap sebagai framework CSS. Anda akan belajar mulai dari setup database MySQL hingga implementasi fitur lengkap dengan interface yang menarik.

🎯 Apa yang Akan Anda Pelajari?

  • Konsep dasar CRUD dan implementasinya
  • Setup database MySQL dan struktur tabel
  • Integrasi PHP dengan MySQL menggunakan PDO
  • Desain responsive dengan Bootstrap 5
  • Validasi form dan error handling
  • Security best practices untuk aplikasi web
  • Pagination dan search functionality

📋 Persiapan dan Requirements

Sebelum memulai membuat CRUD sederhana PHP Bootstrap, pastikan Anda sudah menyiapkan environment development berikut:

🛠️ Tools yang Dibutuhkan

  • XAMPP/WAMP/MAMP: Local server untuk PHP dan MySQL
  • Text Editor: VS Code, Sublime Text, atau Notepad++
  • Web Browser: Chrome, Firefox, atau browser modern lainnya
  • Bootstrap 5: Framework CSS untuk responsive design

📚 Pengetahuan Dasar

  • HTML dan CSS fundamental
  • PHP basic syntax dan functions
  • MySQL database operations
  • Bootstrap grid system (optional tapi recommended)

🗄️ Setup Database MySQL

Langkah pertama dalam membuat aplikasi CRUD adalah menyiapkan database. Mari buat database dan tabel untuk menyimpan data mahasiswa:

📊 Struktur Database

-- Buat database baru
CREATE DATABASE crud_mahasiswa;
USE crud_mahasiswa;

-- Buat tabel mahasiswa
CREATE TABLE mahasiswa (
    id INT(11) AUTO_INCREMENT PRIMARY KEY,
    nim VARCHAR(20) NOT NULL UNIQUE,
    nama VARCHAR(100) NOT NULL,
    email VARCHAR(100) NOT NULL,
    jurusan VARCHAR(50) NOT NULL,
    alamat TEXT,
    tanggal_lahir DATE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

-- Insert sample data
INSERT INTO mahasiswa (nim, nama, email, jurusan, alamat, tanggal_lahir) VALUES
('2021001', 'Ahmad Rizki', 'ahmad.rizki@email.com', 'Teknik Informatika', 'Jakarta Selatan', '2000-05-15'),
('2021002', 'Siti Nurhaliza', 'siti.nurhaliza@email.com', 'Sistem Informasi', 'Bandung', '1999-12-20'),
('2021003', 'Budi Santoso', 'budi.santoso@email.com', 'Teknik Komputer', 'Surabaya', '2001-03-10');

🔧 Konfigurasi Koneksi Database

Buat file konfigurasi untuk menghubungkan PHP dengan MySQL menggunakan PDO (PHP Data Objects) yang lebih secure:

📁 File: config/database.php

<?php
class Database {
    private $host = "localhost";
    private $db_name = "crud_mahasiswa";
    private $username = "root";
    private $password = "";
    public $conn;

    public function getConnection() {
        $this->conn = null;
        
        try {
            $this->conn = new PDO(
                "mysql:host=" . $this->host . ";dbname=" . $this->db_name,
                $this->username,
                $this->password
            );
            $this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            $this->conn->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
        } catch(PDOException $exception) {
            echo "Connection error: " . $exception->getMessage();
        }
        
        return $this->conn;
    }
}
?>

🏗️ Struktur Project

Organisasi file yang baik sangat penting untuk maintainability. Berikut struktur folder yang akan kita gunakan:

crud-mahasiswa/
├── config/
│   └── database.php
├── includes/
│   ├── header.php
│   └── footer.php
├── assets/
│   ├── css/
│   │   └── style.css
│   └── js/
│       └── script.js
├── index.php
├── create.php
├── edit.php
├── delete.php
└── functions.php

🎨 Template HTML dengan Bootstrap

Mari buat template dasar dengan Bootstrap 5 untuk interface yang responsive dan modern:

📁 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 : 'CRUD Mahasiswa'; ?></title>
    
    <!-- Bootstrap 5 CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    
    <!-- Font Awesome Icons -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
    
    <!-- Custom CSS -->
    <link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
    <nav class="navbar navbar-expand-lg navbar-dark bg-primary">
        <div class="container">
            <a class="navbar-brand" href="index.php">
                <i class="fas fa-graduation-cap me-2"></i>
                CRUD Mahasiswa
            </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 ms-auto">
                    <li class="nav-item">
                        <a class="nav-link" href="index.php">
                            <i class="fas fa-home me-1"></i> Home
                        </a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="create.php">
                            <i class="fas fa-plus me-1"></i> Tambah Data
                        </a>
                    </li>
                </ul>
            </div>
        </div>
    </nav>

    <div class="container mt-4">

📁 File: includes/footer.php

    </div> <!-- End container -->

    <footer class="bg-dark text-white text-center py-3 mt-5">
        <div class="container">
            <p class="mb-0">© 2024 CRUD Mahasiswa. Built with PHP & Bootstrap.</p>
        </div>
    </footer>

    <!-- Bootstrap 5 JS -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
    
    <!-- Custom JS -->
    <script src="assets/js/script.js"></script>
</body>
</html>

📖 READ: Menampilkan Data (Index Page)

Halaman utama untuk menampilkan semua data mahasiswa dengan fitur search dan pagination:

📁 File: index.php

<?php
$page_title = "Data Mahasiswa - CRUD PHP Bootstrap";
include_once 'config/database.php';
include_once 'includes/header.php';

// Inisialisasi database
$database = new Database();
$db = $database->getConnection();

// Pagination setup
$records_per_page = 5;
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$offset = ($page - 1) * $records_per_page;

// Search functionality
$search = isset($_GET['search']) ? $_GET['search'] : '';
$search_condition = '';
$params = [];

if (!empty($search)) {
    $search_condition = "WHERE nama LIKE :search OR nim LIKE :search OR jurusan LIKE :search";
    $params[':search'] = "%$search%";
}

// Count total records
$count_query = "SELECT COUNT(*) as total FROM mahasiswa $search_condition";
$count_stmt = $db->prepare($count_query);
$count_stmt->execute($params);
$total_records = $count_stmt->fetch()['total'];
$total_pages = ceil($total_records / $records_per_page);

// Fetch data with pagination
$query = "SELECT * FROM mahasiswa $search_condition ORDER BY created_at DESC LIMIT :limit OFFSET :offset";
$stmt = $db->prepare($query);

foreach ($params as $key => $value) {
    $stmt->bindValue($key, $value);
}
$stmt->bindValue(':limit', $records_per_page, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);

$stmt->execute();
$mahasiswa = $stmt->fetchAll();
?>

<div class="row">
    <div class="col-12">
        <div class="d-flex justify-content-between align-items-center mb-4">
            <h2><i class="fas fa-users me-2"></i>Data Mahasiswa</h2>
            <a href="create.php" class="btn btn-primary">
                <i class="fas fa-plus me-1"></i> Tambah Mahasiswa
            </a>
        </div>

        <!-- Search Form -->
        <div class="card mb-4">
            <div class="card-body">
                <form method="GET" class="row g-3">
                    <div class="col-md-10">
                        <input type="text" class="form-control" name="search" 
                               placeholder="Cari berdasarkan nama, NIM, atau jurusan..." 
                               value="<?php echo htmlspecialchars($search); ?>">
                    </div>
                    <div class="col-md-2">
                        <button type="submit" class="btn btn-outline-primary w-100">
                            <i class="fas fa-search me-1"></i> Cari
                        </button>
                    </div>
                </form>
            </div>
        </div>

        <!-- Data Table -->
        <div class="card">
            <div class="card-body">
                <?php if (count($mahasiswa) > 0): ?>
                    <div class="table-responsive">
                        <table class="table table-striped table-hover">
                            <thead class="table-dark">
                                <tr>
                                    <th>#</th>
                                    <th>NIM</th>
                                    <th>Nama</th>
                                    <th>Email</th>
                                    <th>Jurusan</th>
                                    <th>Tanggal Lahir</th>
                                    <th>Aksi</th>
                                </tr>
                            </thead>
                            <tbody>
                                <?php 
                                $no = $offset + 1;
                                foreach ($mahasiswa as $row): 
                                ?>
                                <tr>
                                    <td><?php echo $no++; ?></td>
                                    <td><span class="badge bg-info"><?php echo htmlspecialchars($row['nim']); ?></span></td>
                                    <td><?php echo htmlspecialchars($row['nama']); ?></td>
                                    <td><?php echo htmlspecialchars($row['email']); ?></td>
                                    <td><?php echo htmlspecialchars($row['jurusan']); ?></td>
                                    <td><?php echo date('d/m/Y', strtotime($row['tanggal_lahir'])); ?></td>
                                    <td>
                                        <div class="btn-group" role="group">
                                            <a href="edit.php?id=<?php echo $row['id']; ?>" 
                                               class="btn btn-sm btn-warning">
                                                <i class="fas fa-edit"></i>
                                            </a>
                                            <a href="delete.php?id=<?php echo $row['id']; ?>" 
                                               class="btn btn-sm btn-danger"
                                               onclick="return confirm('Yakin ingin menghapus data ini?')">
                                                <i class="fas fa-trash"></i>
                                            </a>
                                        </div>
                                    </td>
                                </tr>
                                <?php endforeach; ?>
                            </tbody>
                        </table>
                    </div>

                    <!-- Pagination -->
                    <?php if ($total_pages > 1): ?>
                    <nav aria-label="Page navigation">
                        <ul class="pagination justify-content-center">
                            <?php if ($page > 1): ?>
                            <li class="page-item">
                                <a class="page-link" href="?page=<?php echo ($page-1); ?>&search=<?php echo urlencode($search); ?>">
                                    <i class="fas fa-chevron-left"></i> Previous
                                </a>
                            </li>
                            <?php endif; ?>

                            <?php for ($i = 1; $i <= $total_pages; $i++): ?>
                            <li class="page-item <?php echo ($i == $page) ? 'active' : ''; ?>">
                                <a class="page-link" href="?page=<?php echo $i; ?>&search=<?php echo urlencode($search); ?>">
                                    <?php echo $i; ?>
                                </a>
                            </li>
                            <?php endfor; ?>

                            <?php if ($page < $total_pages): ?>
                            <li class="page-item">
                                <a class="page-link" href="?page=<?php echo ($page+1); ?>&search=<?php echo urlencode($search); ?>">
                                    Next <i class="fas fa-chevron-right"></i>
                                </a>
                            </li>
                            <?php endif; ?>
                        </ul>
                    </nav>
                    <?php endif; ?>

                <?php else: ?>
                    <div class="text-center py-5">
                        <i class="fas fa-inbox fa-3x text-muted mb-3"></i>
                        <h5 class="text-muted">Tidak ada data mahasiswa</h5>
                        <p class="text-muted">Silakan tambah data mahasiswa baru</p>
                        <a href="create.php" class="btn btn-primary">
                            <i class="fas fa-plus me-1"></i> Tambah Mahasiswa
                        </a>
                    </div>
                <?php endif; ?>
            </div>
        </div>
    </div>
</div>

<?php include_once 'includes/footer.php'; ?>

➕ CREATE: Menambah Data Baru

Form untuk menambahkan data mahasiswa baru dengan validasi yang lengkap:

📁 File: create.php

<?php
$page_title = "Tambah Mahasiswa - CRUD PHP Bootstrap";
include_once 'config/database.php';
include_once 'includes/header.php';

$database = new Database();
$db = $database->getConnection();

$errors = [];
$success = false;

if ($_POST) {
    // Validasi input
    $nim = trim($_POST['nim'] ?? '');
    $nama = trim($_POST['nama'] ?? '');
    $email = trim($_POST['email'] ?? '');
    $jurusan = trim($_POST['jurusan'] ?? '');
    $alamat = trim($_POST['alamat'] ?? '');
    $tanggal_lahir = $_POST['tanggal_lahir'] ?? '';

    // Validasi required fields
    if (empty($nim)) $errors[] = "NIM harus diisi";
    if (empty($nama)) $errors[] = "Nama harus diisi";
    if (empty($email)) $errors[] = "Email harus diisi";
    if (empty($jurusan)) $errors[] = "Jurusan harus diisi";
    if (empty($tanggal_lahir)) $errors[] = "Tanggal lahir harus diisi";

    // Validasi format email
    if (!empty($email) && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $errors[] = "Format email tidak valid";
    }

    // Validasi NIM unik
    if (!empty($nim)) {
        $check_query = "SELECT COUNT(*) as count FROM mahasiswa WHERE nim = :nim";
        $check_stmt = $db->prepare($check_query);
        $check_stmt->bindParam(':nim', $nim);
        $check_stmt->execute();
        
        if ($check_stmt->fetch()['count'] > 0) {
            $errors[] = "NIM sudah terdaftar";
        }
    }

    // Jika tidak ada error, simpan data
    if (empty($errors)) {
        try {
            $query = "INSERT INTO mahasiswa (nim, nama, email, jurusan, alamat, tanggal_lahir) 
                      VALUES (:nim, :nama, :email, :jurusan, :alamat, :tanggal_lahir)";
            
            $stmt = $db->prepare($query);
            $stmt->bindParam(':nim', $nim);
            $stmt->bindParam(':nama', $nama);
            $stmt->bindParam(':email', $email);
            $stmt->bindParam(':jurusan', $jurusan);
            $stmt->bindParam(':alamat', $alamat);
            $stmt->bindParam(':tanggal_lahir', $tanggal_lahir);

            if ($stmt->execute()) {
                $success = true;
                // Reset form
                $nim = $nama = $email = $jurusan = $alamat = $tanggal_lahir = '';
            }
        } catch (PDOException $e) {
            $errors[] = "Error: " . $e->getMessage();
        }
    }
}
?>

<div class="row justify-content-center">
    <div class="col-md-8">
        <div class="card">
            <div class="card-header bg-primary text-white">
                <h4 class="mb-0">
                    <i class="fas fa-user-plus me-2"></i>
                    Tambah Data Mahasiswa
                </h4>
            </div>
            <div class="card-body">
                <!-- Success Alert -->
                <?php if ($success): ?>
                <div class="alert alert-success alert-dismissible fade show" role="alert">
                    <i class="fas fa-check-circle me-2"></i>
                    Data mahasiswa berhasil ditambahkan!
                    <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
                </div>
                <?php endif; ?>

                <!-- Error Alerts -->
                <?php if (!empty($errors)): ?>
                <div class="alert alert-danger alert-dismissible fade show" role="alert">
                    <i class="fas fa-exclamation-triangle me-2"></i>
                    <strong>Terjadi kesalahan:</strong>
                    <ul class="mb-0 mt-2">
                        <?php foreach ($errors as $error): ?>
                        <li><?php echo htmlspecialchars($error); ?></li>
                        <?php endforeach; ?>
                    </ul>
                    <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
                </div>
                <?php endif; ?>

                <form method="POST" novalidate>
                    <div class="row">
                        <div class="col-md-6 mb-3">
                            <label for="nim" class="form-label">
                                <i class="fas fa-id-card me-1"></i> NIM <span class="text-danger">*</span>
                            </label>
                            <input type="text" class="form-control" id="nim" name="nim" 
                                   value="<?php echo htmlspecialchars($nim ?? ''); ?>" 
                                   placeholder="Contoh: 2021001" required>
                        </div>
                        
                        <div class="col-md-6 mb-3">
                            <label for="nama" class="form-label">
                                <i class="fas fa-user me-1"></i> Nama Lengkap <span class="text-danger">*</span>
                            </label>
                            <input type="text" class="form-control" id="nama" name="nama" 
                                   value="<?php echo htmlspecialchars($nama ?? ''); ?>" 
                                   placeholder="Masukkan nama lengkap" required>
                        </div>
                    </div>

                    <div class="row">
                        <div class="col-md-6 mb-3">
                            <label for="email" class="form-label">
                                <i class="fas fa-envelope me-1"></i> Email <span class="text-danger">*</span>
                            </label>
                            <input type="email" class="form-control" id="email" name="email" 
                                   value="<?php echo htmlspecialchars($email ?? ''); ?>" 
                                   placeholder="contoh@email.com" required>
                        </div>
                        
                        <div class="col-md-6 mb-3">
                            <label for="jurusan" class="form-label">
                                <i class="fas fa-graduation-cap me-1"></i> Jurusan <span class="text-danger">*</span>
                            </label>
                            <select class="form-select" id="jurusan" name="jurusan" required>
                                <option value="">Pilih Jurusan</option>
                                <option value="Teknik Informatika" <?php echo ($jurusan ?? '') == 'Teknik Informatika' ? 'selected' : ''; ?>>Teknik Informatika</option>
                                <option value="Sistem Informasi" <?php echo ($jurusan ?? '') == 'Sistem Informasi' ? 'selected' : ''; ?>>Sistem Informasi</option>
                                <option value="Teknik Komputer" <?php echo ($jurusan ?? '') == 'Teknik Komputer' ? 'selected' : ''; ?>>Teknik Komputer</option>
                                <option value="Manajemen Informatika" <?php echo ($jurusan ?? '') == 'Manajemen Informatika' ? 'selected' : ''; ?>>Manajemen Informatika</option>
                            </select>
                        </div>
                    </div>

                    <div class="mb-3">
                        <label for="tanggal_lahir" class="form-label">
                            <i class="fas fa-calendar me-1"></i> Tanggal Lahir <span class="text-danger">*</span>
                        </label>
                        <input type="date" class="form-control" id="tanggal_lahir" name="tanggal_lahir" 
                               value="<?php echo htmlspecialchars($tanggal_lahir ?? ''); ?>" required>
                    </div>

                    <div class="mb-3">
                        <label for="alamat" class="form-label">
                            <i class="fas fa-map-marker-alt me-1"></i> Alamat
                        </label>
                        <textarea class="form-control" id="alamat" name="alamat" rows="3" 
                                  placeholder="Masukkan alamat lengkap"><?php echo htmlspecialchars($alamat ?? ''); ?></textarea>
                    </div>

                    <div class="d-grid gap-2 d-md-flex justify-content-md-end">
                        <a href="index.php" class="btn btn-secondary me-md-2">
                            <i class="fas fa-arrow-left me-1"></i> Kembali
                        </a>
                        <button type="submit" class="btn btn-primary">
                            <i class="fas fa-save me-1"></i> Simpan Data
                        </button>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>

<?php include_once 'includes/footer.php'; ?>

✏️ UPDATE: Mengedit Data

Halaman untuk mengedit data mahasiswa yang sudah ada:

📁 File: edit.php

<?php
$page_title = "Edit Mahasiswa - CRUD PHP Bootstrap";
include_once 'config/database.php';
include_once 'includes/header.php';

$database = new Database();
$db = $database->getConnection();

$errors = [];
$success = false;
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;

// Ambil data mahasiswa berdasarkan ID
if ($id > 0) {
    $query = "SELECT * FROM mahasiswa WHERE id = :id";
    $stmt = $db->prepare($query);
    $stmt->bindParam(':id', $id);
    $stmt->execute();
    
    $mahasiswa = $stmt->fetch();
    
    if (!$mahasiswa) {
        header("Location: index.php");
        exit();
    }
} else {
    header("Location: index.php");
    exit();
}

if ($_POST) {
    // Validasi input
    $nim = trim($_POST['nim'] ?? '');
    $nama = trim($_POST['nama'] ?? '');
    $email = trim($_POST['email'] ?? '');
    $jurusan = trim($_POST['jurusan'] ?? '');
    $alamat = trim($_POST['alamat'] ?? '');
    $tanggal_lahir = $_POST['tanggal_lahir'] ?? '';

    // Validasi required fields
    if (empty($nim)) $errors[] = "NIM harus diisi";
    if (empty($nama)) $errors[] = "Nama harus diisi";
    if (empty($email)) $errors[] = "Email harus diisi";
    if (empty($jurusan)) $errors[] = "Jurusan harus diisi";
    if (empty($tanggal_lahir)) $errors[] = "Tanggal lahir harus diisi";

    // Validasi format email
    if (!empty($email) && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $errors[] = "Format email tidak valid";
    }

    // Validasi NIM unik (kecuali untuk data yang sedang diedit)
    if (!empty($nim)) {
        $check_query = "SELECT COUNT(*) as count FROM mahasiswa WHERE nim = :nim AND id != :id";
        $check_stmt = $db->prepare($check_query);
        $check_stmt->bindParam(':nim', $nim);
        $check_stmt->bindParam(':id', $id);
        $check_stmt->execute();
        
        if ($check_stmt->fetch()['count'] > 0) {
            $errors[] = "NIM sudah terdaftar";
        }
    }

    // Jika tidak ada error, update data
    if (empty($errors)) {
        try {
            $query = "UPDATE mahasiswa SET 
                      nim = :nim, 
                      nama = :nama, 
                      email = :email, 
                      jurusan = :jurusan, 
                      alamat = :alamat, 
                      tanggal_lahir = :tanggal_lahir,
                      updated_at = CURRENT_TIMESTAMP
                      WHERE id = :id";
            
            $stmt = $db->prepare($query);
            $stmt->bindParam(':nim', $nim);
            $stmt->bindParam(':nama', $nama);
            $stmt->bindParam(':email', $email);
            $stmt->bindParam(':jurusan', $jurusan);
            $stmt->bindParam(':alamat', $alamat);
            $stmt->bindParam(':tanggal_lahir', $tanggal_lahir);
            $stmt->bindParam(':id', $id);

            if ($stmt->execute()) {
                $success = true;
                // Refresh data
                $mahasiswa['nim'] = $nim;
                $mahasiswa['nama'] = $nama;
                $mahasiswa['email'] = $email;
                $mahasiswa['jurusan'] = $jurusan;
                $mahasiswa['alamat'] = $alamat;
                $mahasiswa['tanggal_lahir'] = $tanggal_lahir;
            }
        } catch (PDOException $e) {
            $errors[] = "Error: " . $e->getMessage();
        }
    }
}
?>

<div class="row justify-content-center">
    <div class="col-md-8">
        <div class="card">
            <div class="card-header bg-warning text-dark">
                <h4 class="mb-0">
                    <i class="fas fa-edit me-2"></i>
                    Edit Data Mahasiswa
                </h4>
            </div>
            <div class="card-body">
                <!-- Success Alert -->
                <?php if ($success): ?>
                <div class="alert alert-success alert-dismissible fade show" role="alert">
                    <i class="fas fa-check-circle me-2"></i>
                    Data mahasiswa berhasil diperbarui!
                    <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
                </div>
                <?php endif; ?>

                <!-- Error Alerts -->
                <?php if (!empty($errors)): ?>
                <div class="alert alert-danger alert-dismissible fade show" role="alert">
                    <i class="fas fa-exclamation-triangle me-2"></i>
                    <strong>Terjadi kesalahan:</strong>
                    <ul class="mb-0 mt-2">
                        <?php foreach ($errors as $error): ?>
                        <li><?php echo htmlspecialchars($error); ?></li>
                        <?php endforeach; ?>
                    </ul>
                    <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
                </div>
                <?php endif; ?>

                <form method="POST" novalidate>
                    <div class="row">
                        <div class="col-md-6 mb-3">
                            <label for="nim" class="form-label">
                                <i class="fas fa-id-card me-1"></i> NIM <span class="text-danger">*</span>
                            </label>
                            <input type="text" class="form-control" id="nim" name="nim" 
                                   value="<?php echo htmlspecialchars($mahasiswa['nim']); ?>" 
                                   placeholder="Contoh: 2021001" required>
                        </div>
                        
                        <div class="col-md-6 mb-3">
                            <label for="nama" class="form-label">
                                <i class="fas fa-user me-1"></i> Nama Lengkap <span class="text-danger">*</span>
                            </label>
                            <input type="text" class="form-control" id="nama" name="nama" 
                                   value="<?php echo htmlspecialchars($mahasiswa['nama']); ?>" 
                                   placeholder="Masukkan nama lengkap" required>
                        </div>
                    </div>

                    <div class="row">
                        <div class="col-md-6 mb-3">
                            <label for="email" class="form-label">
                                <i class="fas fa-envelope me-1"></i> Email <span class="text-danger">*</span>
                            </label>
                            <input type="email" class="form-control" id="email" name="email" 
                                   value="<?php echo htmlspecialchars($mahasiswa['email']); ?>" 
                                   placeholder="contoh@email.com" required>
                        </div>
                        
                        <div class="col-md-6 mb-3">
                            <label for="jurusan" class="form-label">
                                <i class="fas fa-graduation-cap me-1"></i> Jurusan <span class="text-danger">*</span>
                            </label>
                            <select class="form-select" id="jurusan" name="jurusan" required>
                                <option value="">Pilih Jurusan</option>
                                <option value="Teknik Informatika" <?php echo $mahasiswa['jurusan'] == 'Teknik Informatika' ? 'selected' : ''; ?>>Teknik Informatika</option>
                                <option value="Sistem Informasi" <?php echo $mahasiswa['jurusan'] == 'Sistem Informasi' ? 'selected' : ''; ?>>Sistem Informasi</option>
                                <option value="Teknik Komputer" <?php echo $mahasiswa['jurusan'] == 'Teknik Komputer' ? 'selected' : ''; ?>>Teknik Komputer</option>
                                <option value="Manajemen Informatika" <?php echo $mahasiswa['jurusan'] == 'Manajemen Informatika' ? 'selected' : ''; ?>>Manajemen Informatika</option>
                            </select>
                        </div>
                    </div>

                    <div class="mb-3">
                        <label for="tanggal_lahir" class="form-label">
                            <i class="fas fa-calendar me-1"></i> Tanggal Lahir <span class="text-danger">*</span>
                        </label>
                        <input type="date" class="form-control" id="tanggal_lahir" name="tanggal_lahir" 
                               value="<?php echo htmlspecialchars($mahasiswa['tanggal_lahir']); ?>" required>
                    </div>

                    <div class="mb-3">
                        <label for="alamat" class="form-label">
                            <i class="fas fa-map-marker-alt me-1"></i> Alamat
                        </label>
                        <textarea class="form-control" id="alamat" name="alamat" rows="3" 
                                  placeholder="Masukkan alamat lengkap"><?php echo htmlspecialchars($mahasiswa['alamat']); ?></textarea>
                    </div>

                    <div class="d-grid gap-2 d-md-flex justify-content-md-end">
                        <a href="index.php" class="btn btn-secondary me-md-2">
                            <i class="fas fa-arrow-left me-1"></i> Kembali
                        </a>
                        <button type="submit" class="btn btn-warning">
                            <i class="fas fa-save me-1"></i> Update Data
                        </button>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>

<?php include_once 'includes/footer.php'; ?>

🗑️ DELETE: Menghapus Data

Script untuk menghapus data mahasiswa dengan konfirmasi:

📁 File: delete.php

<?php
include_once 'config/database.php';

$database = new Database();
$db = $database->getConnection();

$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;

if ($id > 0) {
    try {
        // Cek apakah data exists
        $check_query = "SELECT nama FROM mahasiswa WHERE id = :id";
        $check_stmt = $db->prepare($check_query);
        $check_stmt->bindParam(':id', $id);
        $check_stmt->execute();
        
        $mahasiswa = $check_stmt->fetch();
        
        if ($mahasiswa) {
            // Hapus data
            $delete_query = "DELETE FROM mahasiswa WHERE id = :id";
            $delete_stmt = $db->prepare($delete_query);
            $delete_stmt->bindParam(':id', $id);
            
            if ($delete_stmt->execute()) {
                // Redirect dengan success message
                header("Location: index.php?deleted=1&nama=" . urlencode($mahasiswa['nama']));
                exit();
            } else {
                // Redirect dengan error message
                header("Location: index.php?error=delete_failed");
                exit();
            }
        } else {
            // Data tidak ditemukan
            header("Location: index.php?error=not_found");
            exit();
        }
    } catch (PDOException $e) {
        // Redirect dengan error message
        header("Location: index.php?error=database_error");
        exit();
    }
} else {
    // ID tidak valid
    header("Location: index.php?error=invalid_id");
    exit();
}
?>

🎨 Custom CSS untuk Styling

Tambahkan styling custom untuk mempercantik tampilan aplikasi:

📁 File: assets/css/style.css

/* Custom Styles for CRUD Application */
:root {
    --primary-color: #0d6efd;
    --secondary-color: #6c757d;
    --success-color: #198754;
    --warning-color: #ffc107;
    --danger-color: #dc3545;
    --info-color: #0dcaf0;
}

body {
    background-color: #f8f9fa;
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}

/* Navbar Styling */
.navbar-brand {
    font-weight: 600;
    font-size: 1.5rem;
}

/* Card Styling */
.card {
    border: none;
    border-radius: 15px;
    box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
    transition: all 0.3s ease;
}

.card:hover {
    box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
    transform: translateY(-2px);
}

.card-header {
    border-radius: 15px 15px 0 0 !important;
    border-bottom: none;
    padding: 1.5rem;
}

/* Table Styling */
.table {
    border-radius: 10px;
    overflow: hidden;
}

.table thead th {
    border-top: none;
    font-weight: 600;
    text-transform: uppercase;
    font-size: 0.875rem;
    letter-spacing: 0.5px;
}

.table tbody tr {
    transition: all 0.3s ease;
}

.table tbody tr:hover {
    background-color: rgba(13, 110, 253, 0.05);
    transform: scale(1.01);
}

/* Button Styling */
.btn {
    border-radius: 8px;
    font-weight: 500;
    padding: 0.5rem 1rem;
    transition: all 0.3s ease;
}

.btn:hover {
    transform: translateY(-1px);
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}

.btn-group .btn {
    margin: 0 2px;
}

/* Form Styling */
.form-control, .form-select {
    border-radius: 8px;
    border: 2px solid #e9ecef;
    padding: 0.75rem;
    transition: all 0.3s ease;
}

.form-control:focus, .form-select:focus {
    border-color: var(--primary-color);
    box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25);
}

.form-label {
    font-weight: 600;
    color: #495057;
    margin-bottom: 0.5rem;
}

/* Alert Styling */
.alert {
    border: none;
    border-radius: 10px;
    padding: 1rem 1.5rem;
}

/* Badge Styling */
.badge {
    font-size: 0.875rem;
    padding: 0.5rem 0.75rem;
    border-radius: 6px;
}

/* Pagination Styling */
.pagination .page-link {
    border-radius: 8px;
    margin: 0 2px;
    border: 2px solid #e9ecef;
    color: var(--primary-color);
    font-weight: 500;
}

.pagination .page-item.active .page-link {
    background-color: var(--primary-color);
    border-color: var(--primary-color);
}

/* Footer Styling */
footer {
    margin-top: auto;
}

/* Responsive Adjustments */
@media (max-width: 768px) {
    .table-responsive {
        border-radius: 10px;
    }
    
    .btn-group {
        display: flex;
        flex-direction: column;
        gap: 5px;
    }
    
    .btn-group .btn {
        margin: 0;
    }
}

/* Loading Animation */
.loading {
    display: inline-block;
    width: 20px;
    height: 20px;
    border: 3px solid rgba(255, 255, 255, 0.3);
    border-radius: 50%;
    border-top-color: #fff;
    animation: spin 1s ease-in-out infinite;
}

@keyframes spin {
    to { transform: rotate(360deg); }
}

/* Success Animation */
.fade-in {
    animation: fadeIn 0.5s ease-in;
}

@keyframes fadeIn {
    from { opacity: 0; transform: translateY(-10px); }
    to { opacity: 1; transform: translateY(0); }
}

⚡ JavaScript untuk Interaktivitas

Tambahkan JavaScript untuk meningkatkan user experience:

📁 File: assets/js/script.js

// CRUD Application JavaScript
document.addEventListener('DOMContentLoaded', function() {
    
    // Auto-hide alerts after 5 seconds
    const alerts = document.querySelectorAll('.alert');
    alerts.forEach(alert => {
        setTimeout(() => {
            if (alert && alert.classList.contains('show')) {
                const bsAlert = new bootstrap.Alert(alert);
                bsAlert.close();
            }
        }, 5000);
    });

    // Form validation
    const forms = document.querySelectorAll('form[novalidate]');
    forms.forEach(form => {
        form.addEventListener('submit', function(event) {
            if (!form.checkValidity()) {
                event.preventDefault();
                event.stopPropagation();
            }
            form.classList.add('was-validated');
        });
    });

    // Confirm delete with better UX
    const deleteLinks = document.querySelectorAll('a[href*="delete.php"]');
    deleteLinks.forEach(link => {
        link.addEventListener('click', function(e) {
            e.preventDefault();
            
            const nama = this.closest('tr').querySelector('td:nth-child(3)').textContent;
            
            if (confirm(`Apakah Anda yakin ingin menghapus data mahasiswa "${nama}"?\n\nTindakan ini tidak dapat dibatalkan.`)) {
                // Show loading state
                this.innerHTML = '';
                this.classList.add('disabled');
                
                // Redirect after short delay for better UX
                setTimeout(() => {
                    window.location.href = this.href;
                }, 500);
            }
        });
    });

    // Search functionality with debounce
    const searchInput = document.querySelector('input[name="search"]');
    if (searchInput) {
        let searchTimeout;
        
        searchInput.addEventListener('input', function() {
            clearTimeout(searchTimeout);
            
            searchTimeout = setTimeout(() => {
                if (this.value.length >= 3 || this.value.length === 0) {
                    this.form.submit();
                }
            }, 500);
        });
    }

    // Auto-format NIM input
    const nimInput = document.querySelector('#nim');
    if (nimInput) {
        nimInput.addEventListener('input', function() {
            // Remove non-numeric characters
            this.value = this.value.replace(/[^0-9]/g, '');
            
            // Limit to 10 characters
            if (this.value.length > 10) {
                this.value = this.value.slice(0, 10);
            }
        });
    }

    // Auto-capitalize name input
    const namaInput = document.querySelector('#nama');
    if (namaInput) {
        namaInput.addEventListener('input', function() {
            // Capitalize first letter of each word
            this.value = this.value.replace(/\b\w/g, l => l.toUpperCase());
        });
    }

    // Email validation feedback
    const emailInput = document.querySelector('#email');
    if (emailInput) {
        emailInput.addEventListener('blur', function() {
            const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
            
            if (this.value && !emailRegex.test(this.value)) {
                this.classList.add('is-invalid');
                
                // Add or update invalid feedback
                let feedback = this.parentNode.querySelector('.invalid-feedback');
                if (!feedback) {
                    feedback = document.createElement('div');
                    feedback.className = 'invalid-feedback';
                    this.parentNode.appendChild(feedback);
                }
                feedback.textContent = 'Format email tidak valid';
            } else {
                this.classList.remove('is-invalid');
                const feedback = this.parentNode.querySelector('.invalid-feedback');
                if (feedback) {
                    feedback.remove();
                }
            }
        });
    }

    // Smooth scroll for pagination
    const paginationLinks = document.querySelectorAll('.pagination a');
    paginationLinks.forEach(link => {
        link.addEventListener('click', function(e) {
            // Add loading state
            this.innerHTML = '';
        });
    });

    // Table row click to highlight
    const tableRows = document.querySelectorAll('tbody tr');
    tableRows.forEach(row => {
        row.addEventListener('click', function() {
            // Remove previous highlights
            tableRows.forEach(r => r.classList.remove('table-active'));
            
            // Add highlight to clicked row
            this.classList.add('table-active');
        });
    });

    // Show success message if data was deleted
    const urlParams = new URLSearchParams(window.location.search);
    if (urlParams.get('deleted') === '1') {
        const nama = urlParams.get('nama');
        showToast(`Data mahasiswa "${nama}" berhasil dihapus`, 'success');
        
        // Clean URL
        window.history.replaceState({}, document.title, window.location.pathname);
    }

    // Toast notification function
    function showToast(message, type = 'info') {
        const toastContainer = getOrCreateToastContainer();
        
        const toast = document.createElement('div');
        toast.className = `toast align-items-center text-white bg-${type} border-0`;
        toast.setAttribute('role', 'alert');
        toast.innerHTML = `

 

${message}

 

`; toastContainer.appendChild(toast); const bsToast = new bootstrap.Toast(toast); bsToast.show(); // Remove toast element after it’s hidden toast.addEventListener(‘hidden.bs.toast’, () => { toast.remove(); }); } function getOrCreateToastContainer() { let container = document.querySelector(‘.toast-container’); if (!container) { container = document.createElement(‘div’); container.className = ‘toast-container position-fixed bottom-0 end-0 p-3’; document.body.appendChild(container); } return container; } });

🔒 Security Best Practices

🛡️ Keamanan Aplikasi Web

1. SQL Injection Prevention
Gunakan prepared statements dengan PDO untuk mencegah SQL injection
2. XSS Protection
Selalu gunakan htmlspecialchars() untuk output data user
3. Input Validation
Validasi semua input di server-side, jangan hanya client-side
4. CSRF Protection
Implementasikan CSRF tokens untuk form submissions
5. Error Handling
Jangan tampilkan error messages yang expose informasi sensitif

🚀 Deployment dan Optimisasi

📦 Persiapan Production

Sebelum deploy ke production, pastikan Anda melakukan optimisasi berikut:

🔧 Konfigurasi Production

  • Database: Ganti credentials database dengan yang production
  • Error Reporting: Matikan error reporting di production
  • HTTPS: Pastikan menggunakan SSL certificate
  • Backup: Setup automated backup untuk database

⚡ Performance Optimization

  • Caching: Implement caching untuk query yang sering digunakan
  • Compression: Enable GZIP compression di server
  • CDN: Gunakan CDN untuk Bootstrap dan Font Awesome
  • Database Indexing: Tambahkan index pada kolom yang sering di-query

🔗 Artikel Terkait

Untuk memperdalam pemahaman PHP dan web development, baca juga artikel-artikel berikut:

❓ FAQ (Frequently Asked Questions)

❓ Apa itu CRUD dan mengapa penting dalam web development?

CRUD adalah singkatan dari Create, Read, Update, Delete – empat operasi dasar dalam pengelolaan data. CRUD penting karena hampir semua aplikasi web membutuhkan kemampuan untuk menambah, membaca, mengubah, dan menghapus data. Ini adalah foundation dari sistem manajemen data.

❓ Mengapa menggunakan PDO daripada mysqli untuk koneksi database?

PDO (PHP Data Objects) lebih fleksibel karena mendukung multiple database engines, memiliki API yang konsisten, dan built-in support untuk prepared statements. PDO juga lebih secure dan mudah untuk switch antar database jika diperlukan.

❓ Bagaimana cara mencegah SQL Injection dalam aplikasi CRUD?

Gunakan prepared statements dengan parameter binding. Jangan pernah concatenate user input langsung ke SQL query. Contoh: $stmt->bindParam(':nama', $nama) lebih aman daripada "SELECT * FROM users WHERE nama = '$nama'".

❓ Apakah Bootstrap wajib untuk membuat aplikasi CRUD?

Tidak wajib, tapi sangat direkomendasikan. Bootstrap mempercepat development dengan menyediakan komponen UI yang sudah jadi dan responsive. Anda bisa menggunakan framework CSS lain seperti Tailwind, Bulma, atau bahkan custom CSS.

❓ Bagaimana cara menambahkan fitur upload file dalam CRUD?

Tambahkan input type=”file” di form, set enctype=”multipart/form-data”, dan handle file upload di PHP menggunakan $_FILES. Pastikan validasi file type, size, dan simpan file di folder yang aman dengan nama yang di-sanitize.

❓ Bagaimana implementasi pagination yang efisien untuk data besar?

Gunakan LIMIT dan OFFSET dalam SQL query, hitung total records untuk pagination info, dan implement caching jika diperlukan. Untuk data sangat besar, consider cursor-based pagination atau infinite scrolling.

❓ Apakah aplikasi CRUD ini sudah production-ready?

Aplikasi ini adalah foundation yang baik, tapi untuk production perlu tambahan: authentication/authorization, logging, error handling yang robust, rate limiting, input sanitization yang lebih ketat, dan security headers.

❓ Bagaimana cara menambahkan fitur export data ke Excel/PDF?

Untuk Excel, gunakan library seperti PhpSpreadsheet. Untuk PDF, gunakan TCPDF atau mPDF. Buat endpoint terpisah untuk export, query data yang diperlukan, dan generate file sesuai format yang diminta user.

🎓 Kesimpulan

Selamat! Anda telah berhasil membuat CRUD sederhana PHP Bootstrap yang lengkap dan functional. Aplikasi ini mencakup semua operasi dasar CRUD dengan interface yang responsive dan user-friendly.

Dari tutorial ini, Anda telah mempelajari:

  • Database Design dan setup MySQL yang proper
  • PHP PDO untuk koneksi database yang secure
  • Bootstrap 5 untuk responsive UI design
  • Form Validation dan error handling
  • Security Best Practices untuk web applications
  • Pagination dan Search functionality
  • Clean Code Structure dan organization

Aplikasi CRUD ini bisa menjadi foundation untuk project yang lebih kompleks seperti sistem manajemen sekolah, inventory management, atau customer relationship management (CRM). Terus kembangkan dengan menambahkan fitur authentication, role management, dan advanced features lainnya!

🚀 Ready to Build Your CRUD Application?

Mulai journey web development Anda dengan project CRUD yang praktis!

💻 Practice Makes Perfect
🎯 Build Real Applications

Leave a Reply

Your email address will not be published. Required fields are marked *