Database dengan PDO — PHP

PDO (PHP Data Objects) adalah cara standar PHP berkomunikasi dengan database. Koneksi (selalu bungkus dengan try/catch): $dsn =…

PDO (PHP Data Objects) adalah cara bawaan PHP untuk bicara dengan database: MySQL, PostgreSQL, SQLite, semuanya lewat method yang sama. Bayangin bon gudang di Warung Bu Tini. Bonnya sudah dicetak: "Ambil ____ dari rak menu". Pelayan cuma mengisi titik-titiknya, dan petugas gudang membaca isian itu sebagai nama barang, gak pernah sebagai perintah baru. Kalaupun pelayan menulis "semua barang" di titik-titik, petugas mencari barang yang namanya persis "semua barang". Itu prepared statement: query-nya dicetak dulu dengan tempat kosong, nilainya diisi belakangan.

🧪 Tebakan cepat: tabel menu punya kolom id, nama, harga. Kamu menjalankan print_r($stmt->fetch()); untuk baris Mie Ayam tanpa mengatur apa-apa. Array-nya berisi 3 isi atau 6 isi? Tebak dulu, jawabannya ada di bagian "Fetch: ambil hasilnya".

Semua contoh di lesson ini memakai tabel menu milik Warung Bu Tini:

id nama harga
1 Nasi Goreng 15000
2 Mie Ayam 12000
3 Es Teh 5000
4 Kerupuk 2000

Koneksi: new PDO

<?php
$dsn = "mysql:host=localhost;dbname=warung;charset=utf8mb4";

try {
    $pdo = new PDO($dsn, "warung_app", "rahasia", [
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    ]);
} catch (PDOException $e) {
    error_log($e->getMessage());
    exit("Layanan sedang gangguan. Coba lagi nanti.");
}

Prepared statement: prepare lalu execute

$stmt = $pdo->prepare("SELECT nama, harga FROM menu WHERE harga <= ?");
$stmt->execute([12000]);
$murah = $stmt->fetchAll();
// Mie Ayam 12000, Es Teh 5000, Kerupuk 2000
$stmt = $pdo->prepare("SELECT nama, harga FROM menu WHERE harga <= :maks AND nama <> :kecuali");
$stmt->execute(["maks" => 12000, "kecuali" => "Kerupuk"]);
// Mie Ayam 12000, Es Teh 5000

Kenapa repot? Bandingkan dengan menempelkan input langsung ke query:

$nama = $_GET["nama"];  // pengunjung mengetik: ' OR '1'='1
$pdo->query("SELECT id, nama FROM menu WHERE nama = '$nama'");
// SQL jadi: ... WHERE nama = '' OR '1'='1'   -> 4 baris, semua menu

$stmt = $pdo->prepare("SELECT id, nama FROM menu WHERE nama = ?");
$stmt->execute([$nama]);
// mencari menu yang namanya persis ' OR '1'='1   -> 0 baris

Input yang ditempel ke string bisa mengubah bentuk query-nya. Itu SQL injection. Dengan placeholder, input selalu diperlakukan sebagai nilai. Serangannya dibahas lengkap di lesson SQL Injection Prevention di track Security. Aturannya di sini sederhana: setiap nilai dari luar masuk lewat ? atau :nama, gak pernah lewat "...$variabel...".

Fetch: ambil hasilnya

$stmt = $pdo->prepare("SELECT id, nama, harga FROM menu WHERE id = ?");
$stmt->execute([2]);
print_r($stmt->fetch());
Array
(
    [id] => 2
    [0] => 2
    [nama] => Mie Ayam
    [1] => Mie Ayam
    [harga] => 12000
    [2] => 12000
)

Enam isi, bukan tiga. Itu jawaban tebakannya. Mode bawaan PDO adalah PDO::FETCH_BOTH: tiap kolom disimpan dua kali, dengan nama kolom dan dengan nomor urutnya. Karena itu PDO::FETCH_ASSOC biasanya dipasang di koneksi, atau ditulis langsung: $stmt->fetch(PDO::FETCH_ASSOC).

Mode fetch() mengembalikan Cara membaca nama
PDO::FETCH_BOTH (bawaan) array, key nama dan nomor $row["nama"] atau $row[1]
PDO::FETCH_ASSOC array, key nama kolom $row["nama"]
PDO::FETCH_NUM array, key nomor $row[1]
PDO::FETCH_OBJ object stdClass $row->nama

Error: PDO::ERRMODE_EXCEPTION

Sejak PHP 8.0, mode error bawaan PDO adalah PDO::ERRMODE_EXCEPTION: query yang gagal melempar PDOException. Salah ketik nama tabel langsung kelihatan:

<?php
$pdo = new PDO("sqlite:warung.db");

$stmt = $pdo->prepare("SELECT nama, harga FROM menuu WHERE harga <= ?");
Fatal error: Uncaught PDOException: SQLSTATE[HY000]: General error: 1 no such table: menuu in /var/www/warung/menu.php:4

(Pesan di atas dari SQLite. MySQL memberi kode dan kalimat yang berbeda untuk kesalahan yang sama.)

💡 Transaction: semua berhasil atau gak sama sekali

Misalkan tabel menu juga punya kolom stok. Pesanan 3 Es Teh berarti dua perubahan: satu baris baru di tabel pesanan, dan stok Es Teh berkurang 3. Keduanya harus jadi bersama, atau gak sama sekali.

try {
    $pdo->beginTransaction();
    $pdo->prepare("INSERT INTO pesanan (menu_id, jumlah) VALUES (?, ?)")->execute([3, 3]);
    $pdo->prepare("UPDATE menu SET stok = stok - ? WHERE id = ?")->execute([3, 3]);
    $pdo->commit();
} catch (PDOException $e) {
    $pdo->rollBack();
    echo "Gagal: " . $e->getMessage();
}

Kalau stok Es Teh tinggal 2 dan kolom stok punya aturan CHECK (stok >= 0), UPDATE-nya gagal dengan SQLSTATE[23000]: Integrity constraint violation: 19 CHECK constraint failed: stok >= 0. rollBack() membatalkan INSERT yang sudah jalan, jadi tabel pesanan tetap kosong dan stok tetap 2. Konsep transaksinya ada di lesson Transaksi di track SQL.

⚠️ Jebakan yang sering ketemu

TL;DR: PDO (PHP Data Objects) menghubungkan PHP ke database lewat new PDO($dsn, $user, $password, $opsi). Nilai dari luar selalu masuk lewat placeholder: $stmt = $pdo->prepare("... WHERE id = ?") lalu $stmt->execute([$id]), atau placeholder bernama :maks dengan ["maks" => 12000]. Menempelkan input ke string query membuka SQL injection. fetch() mengambil satu baris (atau false), fetchAll() semua baris; mode bawaan FETCH_BOTH menyimpan tiap kolom dua kali, jadi pasang PDO::FETCH_ASSOC. Sejak PHP 8.0 PDO::ERRMODE_EXCEPTION sudah bawaan: query yang gagal melempar PDOException, yang dicatat ke log, bukan ditampilkan. Perubahan yang harus jadi bersama dibungkus beginTransaction(), commit(), dan rollBack() kalau gagal.

Yang akan kamu pelajari