Pemrograman Berorientasi Objek PHP: Membangun Aplikasi CRUD dari Nol
SAMPUL & HALAMAN AWAL
Judul: Pemrograman Berorientasi Objek PHP: Membangun Aplikasi CRUD dari Nol
Tujuan Ebook: Siswa mampu memahami konsep PBO dalam PHP dan mengimplementasikannya ke dalam aplikasi CRUD (Create, Read, Update, Delete) yang fungsional dan aman.
Target Pembaca: Pemula yang sudah mengenal dasar PHP dan MySQL.
Struktur Ebook: 12 Bab + Glosarium + Latihan Akhir
BAB 1: DASAR-DASAR PEMROGRAMAN BERORIENTASI OBJEK (PBO)
1.1 Pendahuluan
Apa itu PBO? Pemrograman Berorientasi Objek adalah paradigma pemrograman yang berfokus pada "objek" yang memiliki data (properti) dan perilaku (metode). Bayangkan sebuah mobil: mobil memiliki warna, merek (properti) dan kemampuan berjalan, berhenti (metode).
Perbedaan OOP vs Prosedural:
| Aspek | Prosedural | OOP |
|---|---|---|
| Data | Terpisah dari fungsi | Terbungkus dalam objek |
| Keamanan | Rendah | Tinggi (enkapsulasi) |
| Ulang pakai | Sulit | Mudah (inheritance) |
| Perawatan | Sulit jika besar | Terstruktur |
1.2 Class dan Object
- Class: Cetak biru / template untuk membuat objek
- Object: Instance nyata dari sebuah class
// Membuat Class
class Siswa {
// Properti
public $nama;
public $kelas;
// Method
public function perkenalan() {
return "Halo, nama saya " . $this->nama;
}
}
// Membuat Object
$siswa1 = new Siswa();
$siswa1->nama = "Budi";
echo $siswa1->perkenalan(); // Output: Halo, nama saya Budi
1.3 Property dan Method
- Property: Variabel dalam class
- Method: Fungsi dalam class
1.4 Access Modifier
| Modifier | Class sendiri | Turunan | Luar class |
|---|---|---|---|
| public | ✓ | ✓ | ✓ |
| protected | ✓ | ✓ | ✗ |
| private | ✓ | ✗ | ✗ |
Contoh:
class User {
public $username; // Bisa diakses semua
protected $email; // Hanya class dan turunan
private $password; // Hanya class ini saja
public function setPassword($pass) {
$this->password = password_hash($pass, PASSWORD_DEFAULT);
}
}
1.5 Constructor dan Destructor
- Constructor: Method yang otomatis dijalankan saat objek dibuat
- Destructor: Method yang dijalankan saat objek dihapus
class Koneksi {
private $conn;
public function __construct() {
echo "Koneksi dibuat<br>";
$this->conn = mysqli_connect("localhost", "root", "", "db");
}
public function __destruct() {
echo "Koneksi ditutup<br>";
mysqli_close($this->conn);
}
}
1.6 Latihan Bab 1
-
Buat class
Produkdengan propertinamadanharga -
Tambahkan method
info()yang mengembalikan string deskripsi produk - Buat objek dan tampilkan informasinya
BAB 2: PERSIAPAN LINGKUNGAN DAN DATABASE
2.1 Persiapan Tools
Software yang diperlukan:
- XAMPP (Apache + MySQL + PHP) atau Laragon
- Text Editor (VS Code, Sublime Text, atau PHPStorm)
- Browser (Chrome/Firefox)
Langkah instalasi XAMPP:
- Download dari apachefriends.org
- Install dengan default settings
- Jalankan XAMPP Control Panel
- Start Apache dan MySQL
2.2 Struktur Folder Project
crud_oop/
│
├── config/ # File konfigurasi
├── classes/ # File class OOP
├── views/ # Tampilan HTML
├── assets/ # CSS, JS, Gambar
└── index.php # Entry point
2.3 Membuat Database
Buka phpMyAdmin (http://localhost/phpmyadmin) dan jalankan SQL berikut:
CREATE DATABASE sekolah;
USE sekolah;
CREATE TABLE siswa (
id INT(11) AUTO_INCREMENT PRIMARY KEY,
nis VARCHAR(20) NOT NULL UNIQUE,
nama VARCHAR(100) NOT NULL,
kelas VARCHAR(10) NOT NULL,
alamat TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Contoh data awal
INSERT INTO siswa (nis, nama, kelas, alamat) VALUES
('001', 'Budi Santoso', '10 RPL 1', 'Jl. Merdeka No.1'),
('002', 'Ani Wijaya', '10 RPL 1', 'Jl. Sudirman No.2');
2.4 Latihan Bab 2
- Install XAMPP dan pastikan Apache & MySQL berjalan
- Buat database
perpustakaan -
Buat tabel
bukudengan field: id, judul, pengarang, tahun, stok
BAB 3: MEMBUAT CLASS DATABASE DAN KONEKSI
3.1 Konsep Koneksi Database dengan OOP
Kita akan membuat class khusus untuk menangani koneksi database. Class ini akan reusable untuk seluruh project.
3.2 Membuat Class Database
File: config/Database.php
<?php
class Database {
// Property dengan access modifier private
private $host = "localhost";
private $username = "root";
private $password = "";
private $database = "sekolah";
private $connection;
// Constructor: otomatis menjalankan koneksi
public function __construct() {
$this->connect();
}
// Method untuk koneksi
private function connect() {
$this->connection = new mysqli(
$this->host,
$this->username,
$this->password,
$this->database
);
// Cek error koneksi
if ($this->connection->connect_error) {
die("Koneksi gagal: " . $this->connection->connect_error);
}
// Set charset ke UTF-8
$this->connection->set_charset("utf8");
}
// Method untuk mendapatkan objek koneksi
public function getConnection() {
return $this->connection;
}
// Method untuk menutup koneksi
public function closeConnection() {
if ($this->connection) {
$this->connection->close();
}
}
}
?>
3.3 Penjelasan Kode
-
private $host: property hanya bisa diakses dari dalam class -
__construct(): otomatis memanggilconnect()saat objek dibuat -
getConnection(): mengembalikan koneksi untuk digunakan class lain
3.4 Menguji Koneksi
File: test_koneksi.php
<?php
require_once "config/Database.php";
$db = new Database();
$conn = $db->getConnection();
if ($conn) {
echo "Koneksi berhasil!";
}
$db->closeConnection();
?>
3.5 Latihan Bab 3
- Buat class Database dengan parameter host, user, password, db
- Tambahkan method
query()untuk menjalankan SQL - Uji coba dengan menampilkan data dari tabel siswa
BAB 4: MEMBUAT MODEL (BASE MODEL DAN ENTITY MODEL)
4.1 Konsep Model dalam MVC
Model bertanggung jawab untuk berinteraksi dengan database. Setiap tabel biasanya memiliki satu model.
4.2 Membuat Base Model (Abstract Class)
File: classes/Model.php
<?php
require_once "config/Database.php";
abstract class Model {
protected $db;
protected $table;
protected $primaryKey = "id";
public function __construct() {
$database = new Database();
$this->db = $database->getConnection();
}
// Mendapatkan semua data
public function all() {
$sql = "SELECT * FROM {$this->table}";
$result = $this->db->query($sql);
return $result->fetch_all(MYSQLI_ASSOC);
}
// Mendapatkan data berdasarkan ID
public function find($id) {
$sql = "SELECT * FROM {$this->table} WHERE {$this->primaryKey} = ?";
$stmt = $this->db->prepare($sql);
$stmt->bind_param("i", $id);
$stmt->execute();
$result = $stmt->get_result();
return $result->fetch_assoc();
}
// Menyimpan data
public function create($data) {
$fields = implode(", ", array_keys($data));
$placeholders = ":" . implode(", :", array_keys($data));
$sql = "INSERT INTO {$this->table} ({$fields}) VALUES ({$placeholders})";
$stmt = $this->db->prepare($sql);
// Bind parameter dinamis
foreach ($data as $key => $value) {
$stmt->bindValue(":{$key}", $value);
}
return $stmt->execute();
}
// Mengupdate data
public function update($id, $data) {
$setClause = "";
foreach ($data as $key => $value) {
$setClause .= "{$key} = :{$key}, ";
}
$setClause = rtrim($setClause, ", ");
$sql = "UPDATE {$this->table} SET {$setClause} WHERE {$this->primaryKey} = :id";
$stmt = $this->db->prepare($sql);
$data['id'] = $id;
foreach ($data as $key => $value) {
$stmt->bindValue(":{$key}", $value);
}
return $stmt->execute();
}
// Menghapus data
public function delete($id) {
$sql = "DELETE FROM {$this->table} WHERE {$this->primaryKey} = ?";
$stmt = $this->db->prepare($sql);
$stmt->bind_param("i", $id);
return $stmt->execute();
}
}
?>
4.3 Membuat Model untuk Tabel Siswa
File: classes/SiswaModel.php
<?php
require_once "Model.php";
class SiswaModel extends Model {
protected $table = "siswa";
protected $primaryKey = "id";
// Method tambahan khusus siswa
public function searchByNama($keyword) {
$sql = "SELECT * FROM {$this->table} WHERE nama LIKE ?";
$stmt = $this->db->prepare($sql);
$keyword = "%{$keyword}%";
$stmt->bind_param("s", $keyword);
$stmt->execute();
$result = $stmt->get_result();
return $result->fetch_all(MYSQLI_ASSOC);
}
public function getByKelas($kelas) {
$sql = "SELECT * FROM {$this->table} WHERE kelas = ?";
$stmt = $this->db->prepare($sql);
$stmt->bind_param("s", $kelas);
$stmt->execute();
return $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
}
}
?>
4.4 Latihan Bab 4
- Buat model untuk tabel
bukudari bab sebelumnya - Tambahkan method
getByPengarang($pengarang) - Uji coba method
all()danfind()
BAB 5: MEMBUAT CONTROLLER
5.1 Peran Controller
Controller adalah jembatan antara Model (data) dan View (tampilan). Controller:
- Menerima request dari user
- Memanggil method yang sesuai dari Model
- Menyiapkan data untuk View
- Mengarahkan (redirect) ke halaman yang sesuai
5.2 Membuat Controller Dasar
File: controllers/SiswaController.php
<?php
require_once "classes/SiswaModel.php";
class SiswaController {
private $siswaModel;
public function __construct() {
$this->siswaModel = new SiswaModel();
}
// Menampilkan semua data siswa
public function index() {
$siswa = $this->siswaModel->all();
// Load view dan kirim data
include "views/siswa/index.php";
}
// Menampilkan form tambah data
public function create() {
include "views/siswa/create.php";
}
// Menyimpan data baru
public function store() {
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$data = [
'nis' => $_POST['nis'],
'nama' => $_POST['nama'],
'kelas' => $_POST['kelas'],
'alamat' => $_POST['alamat']
];
if ($this->siswaModel->create($data)) {
header("Location: index.php?action=index&success=Data berhasil ditambahkan");
} else {
header("Location: index.php?action=create&error=Gagal menambahkan data");
}
}
}
// Menampilkan form edit
public function edit($id) {
$siswa = $this->siswaModel->find($id);
include "views/siswa/edit.php";
}
// Memproses update data
public function update($id) {
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$data = [
'nis' => $_POST['nis'],
'nama' => $_POST['nama'],
'kelas' => $_POST['kelas'],
'alamat' => $_POST['alamat']
];
if ($this->siswaModel->update($id, $data)) {
header("Location: index.php?action=index&success=Data berhasil diupdate");
} else {
header("Location: index.php?action=edit&id={$id}&error=Gagal mengupdate");
}
}
}
// Menghapus data
public function destroy($id) {
if ($this->siswaModel->delete($id)) {
header("Location: index.php?action=index&success=Data berhasil dihapus");
} else {
header("Location: index.php?action=index&error=Gagal menghapus");
}
}
}
?>
5.3 Membuat Router Sederhana
File: index.php (Entry Point)
<?php
require_once "controllers/SiswaController.php";
$controller = new SiswaController();
// Mendapatkan action dari URL
$action = isset($_GET['action']) ? $_GET['action'] : 'index';
$id = isset($_GET['id']) ? $_GET['id'] : null;
// Routing
switch ($action) {
case 'index':
$controller->index();
break;
case 'create':
$controller->create();
break;
case 'store':
$controller->store();
break;
case 'edit':
$controller->edit($id);
break;
case 'update':
$controller->update($id);
break;
case 'destroy':
$controller->destroy($id);
break;
default:
$controller->index();
break;
}
?>
5.4 Latihan Bab 5
- Buat controller untuk model Buku
- Buat router untuk mengakses semua action
- Uji coba semua fungsi CRUD melalui URL
BAB 6: MEMBUAT VIEW (TAMPILAN)
6.1 Template Dasar
File: views/templates/header.php
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Aplikasi CRUD Siswa</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.sidebar { min-height: 100vh; background-color: #f8f9fa; }
.content { padding: 20px; }
.alert { margin-top: 20px; }
</style>
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-md-3 sidebar p-3">
<h4>Menu Utama</h4>
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link" href="index.php?action=index">Dashboard</a>
</li>
<li class="nav-item">
<a class="nav-link" href="index.php?action=create">Tambah Siswa</a>
</li>
</ul>
</div>
<div class="col-md-9 content">
File: views/templates/footer.php
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
function confirmDelete(id) {
if (confirm('Apakah Anda yakin ingin menghapus data ini?')) {
window.location.href = 'index.php?action=destroy&id=' + id;
}
}
</script>
</body>
</html>
6.2 View untuk Menampilkan Data (READ)
File: views/siswa/index.php
<?php include "views/templates/header.php"; ?>
<h2>Data Siswa</h2>
<?php if (isset($_GET['success'])): ?>
<div class="alert alert-success"><?= htmlspecialchars($_GET['success']) ?></div>
<?php endif; ?>
<?php if (isset($_GET['error'])): ?>
<div class="alert alert-danger"><?= htmlspecialchars($_GET['error']) ?></div>
<?php endif; ?>
<a href="index.php?action=create" class="btn btn-primary mb-3">Tambah Siswa</a>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>ID</th>
<th>NIS</th>
<th>Nama</th>
<th>Kelas</th>
<th>Alamat</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
<?php foreach ($siswa as $row): ?>
<tr>
<td><?= $row['id'] ?></td>
<td><?= htmlspecialchars($row['nis']) ?></td>
<td><?= htmlspecialchars($row['nama']) ?></td>
<td><?= htmlspecialchars($row['kelas']) ?></td>
<td><?= htmlspecialchars($row['alamat']) ?></td>
<td>
<a href="index.php?action=edit&id=<?= $row['id'] ?>" class="btn btn-warning btn-sm">Edit</a>
<button onclick="confirmDelete(<?= $row['id'] ?>)" class="btn btn-danger btn-sm">Hapus</button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php include "views/templates/footer.php"; ?>
6.3 View untuk Tambah Data (CREATE)
File: views/siswa/create.php
<?php include "views/templates/header.php"; ?>
<h2>Tambah Data Siswa</h2>
<?php if (isset($_GET['error'])): ?>
<div class="alert alert-danger"><?= htmlspecialchars($_GET['error']) ?></div>
<?php endif; ?>
<form method="POST" action="index.php?action=store">
<div class="mb-3">
<label for="nis" class="form-label">NIS</label>
<input type="text" class="form-control" id="nis" name="nis" required>
</div>
<div class="mb-3">
<label for="nama" class="form-label">Nama Lengkap</label>
<input type="text" class="form-control" id="nama" name="nama" required>
</div>
<div class="mb-3">
<label for="kelas" class="form-label">Kelas</label>
<select class="form-control" id="kelas" name="kelas" required>
<option value="">Pilih Kelas</option>
<option value="10 RPL 1">10 RPL 1</option>
<option value="10 RPL 2">10 RPL 2</option>
<option value="11 RPL 1">11 RPL 1</option>
<option value="11 RPL 2">11 RPL 2</option>
</select>
</div>
<div class="mb-3">
<label for="alamat" class="form-label">Alamat</label>
<textarea class="form-control" id="alamat" name="alamat" rows="3"></textarea>
</div>
<button type="submit" class="btn btn-primary">Simpan</button>
<a href="index.php?action=index" class="btn btn-secondary">Batal</a>
</form>
<?php include "views/templates/footer.php"; ?>
6.4 View untuk Edit Data (UPDATE)
File: views/siswa/edit.php
<?php include "views/templates/header.php"; ?>
<h2>Edit Data Siswa</h2>
<?php if (isset($_GET['error'])): ?>
<div class="alert alert-danger"><?= htmlspecialchars($_GET['error']) ?></div>
<?php endif; ?>
<form method="POST" action="index.php?action=update&id=<?= $siswa['id'] ?>">
<div class="mb-3">
<label for="nis" class="form-label">NIS</label>
<input type="text" class="form-control" id="nis" name="nis"
value="<?= htmlspecialchars($siswa['nis']) ?>" required>
</div>
<div class="mb-3">
<label for="nama" class="form-label">Nama Lengkap</label>
<input type="text" class="form-control" id="nama" name="nama"
value="<?= htmlspecialchars($siswa['nama']) ?>" required>
</div>
<div class="mb-3">
<label for="kelas" class="form-label">Kelas</label>
<select class="form-control" id="kelas" name="kelas" required>
<option value="10 RPL 1" <?= $siswa['kelas'] == '10 RPL 1' ? 'selected' : '' ?>>10 RPL 1</option>
<option value="10 RPL 2" <?= $siswa['kelas'] == '10 RPL 2' ? 'selected' : '' ?>>10 RPL 2</option>
<option value="11 RPL 1" <?= $siswa['kelas'] == '11 RPL 1' ? 'selected' : '' ?>>11 RPL 1</option>
<option value="11 RPL 2" <?= $siswa['kelas'] == '11 RPL 2' ? 'selected' : '' ?>>11 RPL 2</option>
</select>
</div>
<div class="mb-3">
<label for="alamat" class="form-label">Alamat</label>
<textarea class="form-control" id="alamat" name="alamat" rows="3"><?= htmlspecialchars($siswa['alamat']) ?></textarea>
</div>
<button type="submit" class="btn btn-primary">Update</button>
<a href="index.php?action=index" class="btn btn-secondary">Batal</a>
</form>
<?php include "views/templates/footer.php"; ?>
6.5 Latihan Bab 6
- Buat tampilan untuk CRUD Buku dengan Bootstrap
- Tambahkan fitur search pada halaman index
- Buat tampilan detail data (show)
BAB 7: FITUR SEARCH DAN PAGINATION
7.1 Membuat Fitur Pencarian
Tambahkan method di SiswaModel.php:
public function search($keyword) {
$sql = "SELECT * FROM {$this->table} WHERE nama LIKE ? OR nis LIKE ? OR kelas LIKE ?";
$stmt = $this->db->prepare($sql);
$keyword = "%{$keyword}%";
$stmt->bind_param("sss", $keyword, $keyword, $keyword);
$stmt->execute();
$result = $stmt->get_result();
return $result->fetch_all(MYSQLI_ASSOC);
}
Tambahkan method di SiswaController.php:
public function search() {
$keyword = isset($_GET['keyword']) ? $_GET['keyword'] : '';
$siswa = $this->siswaModel->search($keyword);
include "views/siswa/index.php";
}
Modifikasi form pencarian di views/siswa/index.php:
<form method="GET" class="mb-3">
<div class="input-group">
<input type="hidden" name="action" value="search">
<input type="text" name="keyword" class="form-control"
placeholder="Cari nama, NIS, atau kelas..."
value="<?= isset($_GET['keyword']) ? htmlspecialchars($_GET['keyword']) : '' ?>">
<button type="submit" class="btn btn-primary">Cari</button>
</div>
</form>
7.2 Membuat Pagination
Tambahkan method di SiswaModel.php:
public function paginate($page = 1, $perPage = 5) {
$offset = ($page - 1) * $perPage;
$sql = "SELECT * FROM {$this->table} LIMIT {$offset}, {$perPage}";
$result = $this->db->query($sql);
$data = $result->fetch_all(MYSQLI_ASSOC);
// Hitung total data
$totalSql = "SELECT COUNT(*) as total FROM {$this->table}";
$totalResult = $this->db->query($totalSql);
$total = $totalResult->fetch_assoc()['total'];
return [
'data' => $data,
'total' => $total,
'page' => $page,
'perPage' => $perPage,
'totalPages' => ceil($total / $perPage)
];
}
Modifikasi method index di controller:
public function index() {
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$pagination = $this->siswaModel->paginate($page, 5);
$siswa = $pagination['data'];
$totalPages = $pagination['totalPages'];
$currentPage = $pagination['page'];
include "views/siswa/index.php";
}
Tambahkan navigasi pagination di view:
<!-- Setelah tabel, tambahkan: -->
<nav aria-label="Page navigation">
<ul class="pagination">
<?php if ($currentPage > 1): ?>
<li class="page-item">
<a class="page-link" href="?action=index&page=<?= $currentPage - 1 ?>">Previous</a>
</li>
<?php endif; ?>
<?php for ($i = 1; $i <= $totalPages; $i++): ?>
<li class="page-item <?= $i == $currentPage ? 'active' : '' ?>">
<a class="page-link" href="?action=index&page=<?= $i ?>"><?= $i ?></a>
</li>
<?php endfor; ?>
<?php if ($currentPage < $totalPages): ?>
<li class="page-item">
<a class="page-link" href="?action=index&page=<?= $currentPage + 1 ?>">Next</a>
</li>
<?php endif; ?>
</ul>
</nav>
7.3 Latihan Bab 7
- Implementasikan fitur search pada aplikasi Buku
- Implementasikan pagination
- Buat fitur filter berdasarkan kategori
BAB 8: KEAMANAN APLIKASI
8.1 SQL Injection Prevention
Jangan pernah lakukan ini:
// BERBAHAYA! Rentan SQL Injection
$sql = "SELECT * FROM users WHERE username = '$_POST[username]'";
Lakukan ini (Prepared Statement):
$sql = "SELECT * FROM users WHERE username = ?";
$stmt = $this->db->prepare($sql);
$stmt->bind_param("s", $_POST['username']);
8.2 XSS Prevention (Cross Site Scripting)
Gunakan htmlspecialchars() saat menampilkan data user:
// Aman
<?= htmlspecialchars($row['nama']) ?>
// Tidak aman (rentan XSS)
<?= $row['nama'] ?>
8.3 Validasi Input
Buat trait atau method validasi:
trait Validator {
public function validate($data, $rules) {
$errors = [];
foreach ($rules as $field => $ruleList) {
$rules_array = explode('|', $ruleList);
foreach ($rules_array as $rule) {
if ($rule == 'required' && empty($data[$field])) {
$errors[$field][] = ucfirst($field) . " wajib diisi";
}
if (strpos($rule, 'min:') === 0) {
$min = explode(':', $rule)[1];
if (strlen($data[$field]) < $min) {
$errors[$field][] = ucfirst($field) . " minimal $min karakter";
}
}
if ($rule == 'email' && !filter_var($data[$field], FILTER_VALIDATE_EMAIL)) {
$errors[$field][] = ucfirst($field) . " harus format email";
}
}
}
return $errors;
}
}
8.4 CSRF Protection
Generate token:
class Security {
public static function generateCSRFToken() {
if (!isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
public static function verifyCSRFToken($token) {
if (!isset($_SESSION['csrf_token']) || $token !== $_SESSION['csrf_token']) {
die("CSRF token tidak valid");
}
return true;
}
}
8.5 Latihan Bab 8
- Implementasikan prepared statement di semua query
- Tambahkan validasi untuk form tambah dan edit
- Implementasikan CSRF protection
BAB 9: SESSION DAN FLASH MESSAGE
9.1 Mengelola Session
File: classes/Session.php
<?php
class Session {
public static function start() {
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
}
public static function set($key, $value) {
$_SESSION[$key] = $value;
}
public static function get($key) {
return isset($_SESSION[$key]) ? $_SESSION[$key] : null;
}
public static function has($key) {
return isset($_SESSION[$key]);
}
public static function remove($key) {
if (isset($_SESSION[$key])) {
unset($_SESSION[$key]);
}
}
public static function destroy() {
session_destroy();
$_SESSION = [];
}
// Flash message: pesan yang hanya muncul sekali
public static function flash($key, $message = null) {
if ($message) {
$_SESSION['flash_' . $key] = $message;
} else {
$flash = isset($_SESSION['flash_' . $key]) ? $_SESSION['flash_' . $key] : null;
unset($_SESSION['flash_' . $key]);
return $flash;
}
}
}
?>
9.2 Menggunakan Flash Message di Controller
// Di controller
Session::start();
Session::flash('success', 'Data siswa berhasil ditambahkan');
header("Location: index.php?action=index");
9.3 Menampilkan Flash Message di View
<!-- Di header.php atau index.php -->
<?php
Session::start();
$success = Session::flash('success');
$error = Session::flash('error');
if ($success): ?>
<div class="alert alert-success alert-dismissible fade show" role="alert">
<?= htmlspecialchars($success) ?>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
<?php endif; ?>
<?php if ($error): ?>
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<?= htmlspecialchars($error) ?>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
<?php endif; ?>
9.4 Latihan Bab 9
- Implementasikan session untuk menyimpan data user login
- Gunakan flash message untuk semua notifikasi CRUD
- Buat halaman login sederhana
BAB 10: SISTEM AUTENTIKASI (LOGIN/REGISTER)
10.1 Membuat Tabel Users
CREATE TABLE users (
id INT(11) AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
role ENUM('admin', 'user') DEFAULT 'user',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
10.2 Membuat User Model
File: classes/UserModel.php
<?php
require_once "Model.php";
class UserModel extends Model {
protected $table = "users";
public function register($username, $email, $password) {
// Cek apakah username atau email sudah ada
$checkSql = "SELECT * FROM {$this->table} WHERE username = ? OR email = ?";
$checkStmt = $this->db->prepare($checkSql);
$checkStmt->bind_param("ss", $username, $email);
$checkStmt->execute();
$result = $checkStmt->get_result();
if ($result->num_rows > 0) {
return false; // Username atau email sudah terdaftar
}
// Hash password
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
// Insert user baru
$sql = "INSERT INTO {$this->table} (username, email, password) VALUES (?, ?, ?)";
$stmt = $this->db->prepare($sql);
$stmt->bind_param("sss", $username, $email, $hashedPassword);
return $stmt->execute();
}
public function login($username, $password) {
$sql = "SELECT * FROM {$this->table} WHERE username = ? OR email = ?";
$stmt = $this->db->prepare($sql);
$stmt->bind_param("ss", $username, $username);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows === 1) {
$user = $result->fetch_assoc();
if (password_verify($password, $user['password'])) {
unset($user['password']); // Hapus password dari session
return $user;
}
}
return false;
}
}
?>
10.3 Membuat Auth Controller
File: controllers/AuthController.php
<?php
require_once "classes/UserModel.php";
require_once "classes/Session.php";
class AuthController {
private $userModel;
public function __construct() {
$this->userModel = new UserModel();
Session::start();
}
public function showLogin() {
include "views/auth/login.php";
}
public function login() {
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'];
$password = $_POST['password'];
$user = $this->userModel->login($username, $password);
if ($user) {
Session::set('user', $user);
Session::flash('success', 'Selamat datang, ' . $user['username']);
header("Location: index.php?action=dashboard");
} else {
Session::flash('error', 'Username atau password salah');
header("Location: index.php?action=login");
}
}
}
public function showRegister() {
include "views/auth/register.php";
}
public function register() {
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'];
$email = $_POST['email'];
$password = $_POST['password'];
$confirmPassword = $_POST['confirm_password'];
// Validasi password match
if ($password !== $confirmPassword) {
Session::flash('error', 'Password tidak cocok');
header("Location: index.php?action=register");
return;
}
// Validasi panjang password
if (strlen($password) < 6) {
Session::flash('error', 'Password minimal 6 karakter');
header("Location: index.php?action=register");
return;
}
if ($this->userModel->register($username, $email, $password)) {
Session::flash('success', 'Registrasi berhasil! Silakan login');
header("Location: index.php?action=login");
} else {
Session::flash('error', 'Username atau email sudah terdaftar');
header("Location: index.php?action=register");
}
}
}
public function logout() {
Session::destroy();
header("Location: index.php?action=login");
}
// Middleware untuk cek login
public static function checkAuth() {
Session::start();
if (!Session::has('user')) {
header("Location: index.php?action=login");
exit();
}
}
// Middleware untuk cek role admin
public static function checkAdmin() {
self::checkAuth();
$user = Session::get('user');
if ($user['role'] !== 'admin') {
header("Location: index.php?action=dashboard");
exit();
}
}
}
?>
10.4 View Login dan Register
File: views/auth/login.php
<?php Session::start(); ?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>Login - Aplikasi CRUD</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h4 class="text-center">Login</h4>
</div>
<div class="card-body">
<?php $error = Session::flash('error'); ?>
<?php if ($error): ?>
<div class="alert alert-danger"><?= $error ?></div>
<?php endif; ?>
<form method="POST" action="index.php?action=doLogin">
<div class="mb-3">
<label>Username atau Email</label>
<input type="text" name="username" class="form-control" required>
</div>
<div class="mb-3">
<label>Password</label>
<input type="password" name="password" class="form-control" required>
</div>
<button type="submit" class="btn btn-primary w-100">Login</button>
</form>
<div class="text-center mt-3">
<a href="index.php?action=register">Belum punya akun? Daftar</a>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
10.5 Melindungi Route
Modifikasi index.php router:
<?php
require_once "controllers/SiswaController.php";
require_once "controllers/AuthController.php";
require_once "classes/Session.php";
Session::start();
$action = isset($_GET['action']) ? $_GET['action'] : 'login';
// Route publik (tanpa login)
$publicRoutes = ['login', 'doLogin', 'register', 'doRegister'];
if (!in_array($action, $publicRoutes) && !Session::has('user')) {
header("Location: index.php?action=login");
exit();
}
switch ($action) {
case 'login':
$controller = new AuthController();
$controller->showLogin();
break;
case 'doLogin':
$controller = new AuthController();
$controller->login();
break;
case 'register':
$controller = new AuthController();
$controller->showRegister();
break;
case 'doRegister':
$controller = new AuthController();
$controller->register();
break;
case 'logout':
$controller = new AuthController();
$controller->logout();
break;
case 'dashboard':
include "views/dashboard.php";
break;
// Route CRUD siswa (perlu login)
case 'index':
case 'create':
case 'store':
case 'edit':
case 'update':
case 'destroy':
case 'search':
$controller = new SiswaController();
$action($controller);
break;
default:
header("Location: index.php?action=dashboard");
break;
}
?>
10.6 Latihan Bab 10
- Buat sistem registrasi dan login lengkap
- Implementasikan middleware untuk melindungi halaman CRUD
- Buat fitur "Remember Me" dengan cookie
BAB 11: UPLOAD FILE
11.1 Membuat Tabel dengan Kolom Gambar
ALTER TABLE siswa ADD COLUMN foto VARCHAR(255) DEFAULT NULL;
11.2 Membuat Class Upload Handler
File: classes/Upload.php
<?php
class Upload {
private $allowedTypes = ['image/jpeg', 'image/png', 'image/jpg', 'image/gif'];
private $maxSize = 2097152; // 2MB
private $uploadDir;
public function __construct($uploadDir = "uploads/") {
$this->uploadDir = $uploadDir;
if (!file_exists($this->uploadDir)) {
mkdir($this->uploadDir, 0777, true);
}
}
public function upload($file, $oldFile = null) {
// Cek error upload
if ($file['error'] !== UPLOAD_ERR_OK) {
return ['success' => false, 'message' => 'Gagal upload file'];
}
// Cek tipe file
if (!in_array($file['type'], $this->allowedTypes)) {
return ['success' => false, 'message' => 'Tipe file tidak diizinkan'];
}
// Cek ukuran file
if ($file['size'] > $this->maxSize) {
return ['success' => false, 'message' => 'Ukuran file maksimal 2MB'];
}
// Generate nama file unik
$extension = pathinfo($file['name'], PATHINFO_EXTENSION);
$newFileName = uniqid() . '.' . $extension;
$destination = $this->uploadDir . $newFileName;
// Upload file
if (move_uploaded_file($file['tmp_name'], $destination)) {
// Hapus file lama jika ada
if ($oldFile && file_exists($this->uploadDir . $oldFile)) {
unlink($this->uploadDir . $oldFile);
}
return ['success' => true, 'filename' => $newFileName];
}
return ['success' => false, 'message' => 'Gagal memindahkan file'];
}
public function delete($filename) {
$filePath = $this->uploadDir . $filename;
if (file_exists($filePath)) {
return unlink($filePath);
}
return false;
}
}
?>
11.3 Update Model untuk Mendukung Upload
Modifikasi SiswaModel.php:
public function create($data) {
$sql = "INSERT INTO {$this->table} (nis, nama, kelas, alamat, foto) VALUES (?, ?, ?, ?, ?)";
$stmt = $this->db->prepare($sql);
$stmt->bind_param("sssss",
$data['nis'],
$data['nama'],
$data['kelas'],
$data['alamat'],
$data['foto']
);
return $stmt->execute();
}
public function update($id, $data) {
$sql = "UPDATE {$this->table} SET nis=?, nama=?, kelas=?, alamat=?, foto=? WHERE id=?";
$stmt = $this->db->prepare($sql);
$stmt->bind_param("sssssi",
$data['nis'],
$data['nama'],
$data['kelas'],
$data['alamat'],
$data['foto'],
$id
);
return $stmt->execute();
}
11.4 Update Controller untuk Upload
Modifikasi method store dan update di
SiswaController.php:
public function store() {
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Handle upload foto
$foto = null;
if (isset($_FILES['foto']) && $_FILES['foto']['error'] === UPLOAD_ERR_OK) {
$upload = new Upload();
$result = $upload->upload($_FILES['foto']);
if ($result['success']) {
$foto = $result['filename'];
} else {
Session::flash('error', $result['message']);
header("Location: index.php?action=create");
return;
}
}
$data = [
'nis' => $_POST['nis'],
'nama' => $_POST['nama'],
'kelas' => $_POST['kelas'],
'alamat' => $_POST['alamat'],
'foto' => $foto
];
if ($this->siswaModel->create($data)) {
Session::flash('success', 'Data berhasil ditambahkan');
header("Location: index.php?action=index");
} else {
Session::flash('error', 'Gagal menambahkan data');
header("Location: index.php?action=create");
}
}
}
public function update($id) {
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Ambil data lama untuk mendapatkan foto lama
$oldData = $this->siswaModel->find($id);
$foto = $oldData['foto'];
// Handle upload foto baru
if (isset($_FILES['foto']) && $_FILES['foto']['error'] === UPLOAD_ERR_OK) {
$upload = new Upload();
$result = $upload->upload($_FILES['foto'], $oldData['foto']);
if ($result['success']) {
$foto = $result['filename'];
}
}
$data = [
'nis' => $_POST['nis'],
'nama' => $_POST['nama'],
'kelas' => $_POST['kelas'],
'alamat' => $_POST['alamat'],
'foto' => $foto
];
if ($this->siswaModel->update($id, $data)) {
Session::flash('success', 'Data berhasil diupdate');
header("Location: index.php?action=index");
} else {
Session::flash('error', 'Gagal mengupdate data');
header("Location: index.php?action=edit&id={$id}");
}
}
}
public function destroy($id) {
// Ambil foto sebelum delete
$siswa = $this->siswaModel->find($id);
if ($this->siswaModel->delete($id)) {
// Hapus file foto jika ada
if ($siswa['foto']) {
$upload = new Upload();
$upload->delete($siswa['foto']);
}
Session::flash('success', 'Data berhasil dihapus');
} else {
Session::flash('error', 'Gagal menghapus data');
}
header("Location: index.php?action=index");
}
11.5 Update View dengan Input File
Tambahkan di create.php dan edit.php:
<div class="mb-3">
<label for="foto" class="form-label">Foto</label>
<?php if (isset($siswa) && $siswa['foto']): ?>
<div class="mb-2">
<img src="uploads/<?= $siswa['foto'] ?>" width="100" class="img-thumbnail">
</div>
<?php endif; ?>
<input type="file" class="form-control" id="foto" name="foto" accept="image/*">
<small class="text-muted">Format: JPG, PNG, GIF. Maksimal 2MB</small>
</div>
Update form tag (wajib tambahkan enctype):
<form method="POST" action="..." enctype="multipart/form-data">
Tampilkan foto di index.php:
<th>Foto</th>
// ...
<td>
<?php if ($row['foto']): ?>
<img src="uploads/<?= $row['foto'] ?>" width="50" height="50" class="rounded-circle">
<?php else: ?>
<span class="text-muted">No photo</span>
<?php endif; ?>
</td>
11.6 Latihan Bab 11
- Implementasikan upload foto untuk data siswa
- Buat validasi tipe dan ukuran file
- Tambahkan fitur preview gambar sebelum upload
BAB 12: FINAL PROJECT - APLIKASI PERPUSTAKAAN
12.1 Deskripsi Project
Buat aplikasi perpustakaan lengkap dengan fitur:
- Manajemen Buku (CRUD + upload cover)
- Manajemen Anggota (CRUD + foto)
- Manajemen Peminjaman
- Laporan Peminjaman
- Dashboard dengan statistik
12.2 Struktur Database
-- Tabel buku
CREATE TABLE buku (
id INT AUTO_INCREMENT PRIMARY KEY,
kode_buku VARCHAR(20) UNIQUE,
judul VARCHAR(200),
pengarang VARCHAR(100),
penerbit VARCHAR(100),
tahun INT,
stok INT DEFAULT 1,
cover VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Tabel anggota (mirip dengan siswa)
CREATE TABLE anggota (
id INT AUTO_INCREMENT PRIMARY KEY,
kode_anggota VARCHAR(20) UNIQUE,
nama VARCHAR(100),
alamat TEXT,
no_telp VARCHAR(15),
foto VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Tabel peminjaman
CREATE TABLE peminjaman (
id INT AUTO_INCREMENT PRIMARY KEY,
kode_peminjaman VARCHAR(20) UNIQUE,
buku_id INT,
anggota_id INT,
tanggal_pinjam DATE,
tanggal_kembali DATE,
tanggal_pengembalian DATE NULL,
status ENUM('dipinjam', 'dikembalikan', 'terlambat') DEFAULT 'dipinjam',
denda INT DEFAULT 0,
FOREIGN KEY (buku_id) REFERENCES buku(id) ON DELETE CASCADE,
FOREIGN KEY (anggota_id) REFERENCES anggota(id) ON DELETE CASCADE
);
12.3 Fitur yang Harus Ada
- CRUD Buku dengan upload cover
- CRUD Anggota dengan upload foto
-
Transaksi Peminjaman
- Pinjam buku (kurangi stok)
- Kembalikan buku (tambah stok, hitung denda)
-
Dashboard
- Total buku
- Total anggota
- Total peminjaman aktif
- Grafik sederhana
-
Laporan
- Laporan peminjaman per periode
- Export ke Excel
- Pencarian dan Filter untuk semua data
12.4 Panduan Pengerjaan
- Minggu 1: Setup database dan membuat model untuk Buku, Anggota, Peminjaman
- Minggu 2: Membuat controller dan view untuk Buku & Anggota
- Minggu 3: Implementasi transaksi peminjaman dan pengembalian
- Minggu 4: Dashboard, laporan, dan finishing
12.5 Kriteria Penilaian
| Kriteria | Bobot |
|---|---|
| Fungsionalitas CRUD | 25% |
| Implementasi OOP dengan benar | 20% |
| Keamanan (SQL injection, XSS) | 15% |
| User Interface & UX | 15% |
| Fitur tambahan (upload, export) | 15% |
| Dokumentasi kode | 10% |
GLOSARIUM
| Istilah | Arti |
|---|---|
| Class | Cetak biru atau template untuk membuat objek |
| Object | Instance nyata dari sebuah class |
| Property | Variabel yang berada dalam class |
| Method | Fungsi yang berada dalam class |
| Constructor | Method yang otomatis dijalankan saat objek dibuat |
| Inheritance | Pewarisan properti dan method dari class parent ke child |
| Encapsulation | Pembungkusan data dan method dalam class |
| Polymorphism | Kemampuan method untuk memiliki banyak bentuk |
| MVC | Model-View-Controller, pola arsitektur aplikasi |
| CRUD | Create, Read, Update, Delete - operasi dasar database |
| Prepared Statement | Teknik untuk mencegah SQL injection |
| XSS | Cross Site Scripting, serangan melalui injeksi script |
| CSRF | Cross Site Request Forgery, serangan pemalsuan request |
| Session | Mekanisme menyimpan data user sementara |
| Middleware | Lapisan antara request dan response untuk validasi |
DAFTAR PUSTAKA
- PHP Documentation - php.net
- MySQL Documentation - mysql.com
- Bootstrap Documentation - getbootstrap.com
- "Object-Oriented PHP" oleh Peter Lavin (No Starch Press)
- "PHP and MySQL Web Development" oleh Luke Welling & Laura Thomson
Selamat Belajar! Praktikkan setiap kode, jangan hanya membaca. Semakin sering Anda mengetik kode, semakin cepat Anda menguasai PBO PHP.
Gabung dalam percakapan