Chapter sebelumnya memetakan ekstensi dan driver database di PHP; sekarang kita bedah aktor utamanya. PDO (PHP Data Objects) adalah antarmuka akses database yang seragam: satu set class dan method untuk SQLite, MySQL, PostgreSQL, dan driver lainnya. Tanpa PDO, berpindah database berarti menulis ulang seluruh panggilan akses data; dengan PDO, kode program cukup mengganti string koneksi. Pada chapter ini kita pelajari masalah yang diselesaikan PDO, tiga metode intinya, sekilas bentuk hasil dan mode error, serta alasan PDO menjadi standar de facto di ekosistem PHP modern.

Masalah yang Diselesaikan PDO #

Bayangkan program yang mungkin saja dipindahkan dari MySQL ke PostgreSQL. Dengan ekstensi native, tiap database punya nama fungsi sendiri untuk pekerjaan yang sama, seperti ilustrasi berikut:

php
<?php
// Tanpa PDO: setiap database memakai nama fungsi yang berbeda
$hasilMysqli = mysqli_query($koneksi, "SELECT * FROM produk");
$hasilPgsql  = pg_query($koneksi, "SELECT * FROM produk");

// Dengan PDO: satu panggilan untuk semua driver database
$hasilPdo = $pdo->query("SELECT * FROM produk");

Contoh di atas hanya ilustrasi perbandingan nama fungsi (tidak dimaksudkan untuk dijalankan). Dua baris pertama memanggil fungsi milik dua ekstensi berbeda untuk pekerjaan yang persis sama; baris terakhir memakai PDO yang bekerja identik di semua driver. Perbedaan ini mungkin tampak kecil untuk satu panggilan, tetapi aplikasi nyata memiliki ratusan panggilan akses data — menjalankan query, mengambil baris, prepared statements, transactions, dan penanganan error. PDO menyatukan semuanya dalam dua class (PDO untuk koneksi dan PDOStatement untuk hasil) sehingga logika program terfokus pada data, bukan pada keanehan tiap database.

Tiga Metode Inti PDO #

Hampir semua pekerjaan dengan PDO berputar di sekitar tiga metode. Pilihannya ditentukan oleh dua pertanyaan: apakah SQL menghasilkan baris data, dan apakah SQL perlu menerima nilai dari luar?

MetodeUntukNilai kembali
exec()SQL tanpa baris hasil: CREATE, INSERT, UPDATE, DELETEjumlah baris yang terpengaruh
query()SELECT langsung tanpa input eksternalPDOStatement yang bisa diiterasi
prepare()SQL dengan placeholder untuk data eksternalPDOStatement siap execute()

Mari kita lihat ketiganya bekerja dalam satu program utuh di atas SQLite:

php
<?php
$pdo = new PDO("sqlite::memory:");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);

// exec() untuk SQL tanpa hasil: CREATE TABLE dan INSERT
$pdo->exec("CREATE TABLE tugas (id INTEGER PRIMARY KEY, judul TEXT, selesai INTEGER)");

$jumlah = $pdo->exec("INSERT INTO tugas (judul, selesai) VALUES
    ('Belajar PDO', 0),
    ('Belajar SQL', 1)");
echo "exec() menambahkan $jumlah baris\n";

// query() untuk SELECT: hasilnya bisa langsung diiterasi
echo "query() mengembalikan:\n";
foreach ($pdo->query("SELECT id, judul, selesai FROM tugas ORDER BY id") as $tugas) {
    $status = $tugas["selesai"] === 1 ? "selesai" : "belum";
    echo "  [{$tugas['id']}] {$tugas['judul']} ($status)\n";
}

Output yang Dihasilkan:

text
exec() menambahkan 2 baris
query() mengembalikan:
  [1] Belajar PDO (belum)
  [2] Belajar SQL (selesai)

Telaah baris per baris:

  1. setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION): kesalahan SQL melempar PDOException alih-alih diam — mode produksi yang wajib ada (detailnya di bawah).
  2. setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC): setiap baris hasil berupa array asosiatif, sehingga $tugas['judul'] bisa langsung dipakai.
  3. exec("CREATE TABLE ..."): perintah DDL tidak menghasilkan baris, jadi exec() adalah pilihan yang tepat.
  4. exec("INSERT ...") mengembalikan 2: jumlah baris yang benar-benar disisipkan, langsung tercetak oleh echo.
  5. foreach ($pdo->query(...) ...): query() mengembalikan PDOStatement yang menerapkan Iterator, sehingga bisa diiterasi foreach tanpa loop manual.

Menyisipkan Data Eksternal dengan prepare #

Data yang berasal dari luar program — input formulir, parameter URL, isi berkas — tidak boleh dirangkai langsung ke string SQL. Metode prepare() menyediakan placeholder dalam SQL, lalu execute() mengirim nilainya terpisah:

php
<?php
$pdo = new PDO("sqlite::memory:");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$pdo->exec("CREATE TABLE pengguna (id INTEGER PRIMARY KEY, nama TEXT, email TEXT)");

// prepare() satu kali untuk template INSERT dengan dua placeholder
$stmt = $pdo->prepare("INSERT INTO pengguna (nama, email) VALUES (?, ?)");
$stmt->execute(["Budi", "[email protected]"]);
$stmt->execute(["Siti", "[email protected]"]);

// prepare() juga bekerja untuk SELECT dengan placeholder
$stmt = $pdo->prepare("SELECT nama, email FROM pengguna WHERE nama = ?");
$stmt->execute(["Siti"]);
$pengguna = $stmt->fetch();

echo "Ditemukan: {$pengguna['nama']} <{$pengguna['email']}>\n";

Output yang Dihasilkan:

text
Ditemukan: Siti <[email protected]>

Template "INSERT ... VALUES (?, ?)" disiapkan sekali, lalu execute() menjalankannya dua kali dengan isi array yang berbeda — dua baris masuk dari satu template. Placeholder ? menjamin nilai array diperlakukan sebagai data murni, sehingga tanda kutip atau karakter khusus pada input tidak akan pernah mengubah struktur SQL. Alasan keamanan dan performa ini dibedah tuntas pada chapter Prepared Statements , termasuk placeholder bernama dan teknik binding.

Bentuk Hasil: Preview Fetch Mode #

PDOStatement menawarkan beberapa bentuk baris hasil lewat fetch mode. Dua yang paling sering dipakai: PDO::FETCH_ASSOC mengembalikan array asosiatif, dan PDO::FETCH_OBJ mengembalikan objek anonim dengan nama kolom sebagai property:

php
<?php
$pdo = new PDO("sqlite::memory:");
$pdo->exec("CREATE TABLE bahasa (nama TEXT, tahun INTEGER)");
$pdo->exec("INSERT INTO bahasa (nama, tahun) VALUES ('PHP', 1995), ('SQL', 1974)");

$barisArray = $pdo->query("SELECT nama, tahun FROM bahasa WHERE nama = 'PHP'")
    ->fetch(PDO::FETCH_ASSOC);
$barisObject = $pdo->query("SELECT nama, tahun FROM bahasa WHERE nama = 'SQL'")
    ->fetch(PDO::FETCH_OBJ);

echo "Array  : {$barisArray['nama']} muncul tahun {$barisArray['tahun']}\n";
echo "Object : {$barisObject->nama} muncul tahun {$barisObject->tahun}\n";

Output yang Dihasilkan:

text
Array  : PHP muncul tahun 1995
Object : SQL muncul tahun 1974

Method fetch() mengambil satu baris dari hasil dengan mode yang bisa diberikan per panggilan, seperti contoh di atas. Pilihan mode lain — FETCH_NUM (array numerik), FETCH_BOTH (default: gabungan kunci teks dan angka), hingga FETCH_CLASS (mengisi objek class buatan sendiri) — beserta fetchAll() dan fetchColumn() dibahas lengkap pada chapter Fetching Results di section ini.

Menangani Kesalahan SQL: Preview Mode Error #

Secara bawaan PDO memilih diam: query yang gagal hanya mengisi kode error tanpa mengeluarkan peringatan. Mode PDO::ERRMODE_EXCEPTION mengubahnya menjadi PDOException yang bisa ditangkap try/catch seperti error PHP lainnya:

php
<?php
$pdo = new PDO("sqlite::memory:");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

try {
    $pdo->query("SELECT kolomTidakAda FROM tabelTidakAda");
    echo "Query berhasil dijalankan\n";
} catch (PDOException $e) {
    echo "Query gagal: " . $e->getMessage() . "\n";
}

Output yang Dihasilkan:

text
Query gagal: SQLSTATE[HY000]: General error: 1 no such table: tabelTidakAda

Query sengaja menunjuk tabel dan kolom yang tidak ada, dan PDOException membawa pesan dari database (no such table) lengkap dengan kode SQLSTATE. Tiga mode error (ERRMODE_SILENT, ERRMODE_WARNING, ERRMODE_EXCEPTION), cara membaca errorInfo(), dan strategi menangkap galat dengan rapi dibahas pada chapter Database Errors di section ini.

PDO di Proyek Nyata #

Dalam proyek nyata, PDO hampir selalu menjadi fondasi akses data. Framework Laravel dan Symfony membangun lapisan database-nya di atas PDO, dan package seperti Doctrine DBAL juga berdiri di atasnya. Alasannya bukan tren: API seragam memudahkan pengujian (cukup ganti DSN ke SQLite memori saat unit test), prepared statements tersedia di semua driver, dan kode tidak terkunci pada satu vendor database. Sepanjang section ini kita akan terus memperdalam PDO — parameter binding, fetching, transactions, hingga pola repository yang mengisolasi akses data dari logika bisnis.

Ringkasan #

PDO menyatukan akses ke semua database dalam dua class: PDO untuk koneksi dan PDOStatement untuk hasil. Tiga metode intinya jelas perannya — exec() untuk SQL tanpa baris hasil, query() untuk SELECT langsung, dan prepare() untuk SQL yang menerima data eksternal. Fetch mode dan mode error masing-masing punya babannya sendiri di section ini. Sebelum memperdalam itu semua, langkah pertama setiap program PDO adalah membuka koneksi dengan DSN yang benar — itu topik chapter PDO Connections .

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

contoh.php

Unduh berkas
php
<?php
/**
 * contoh.php - rangkuman Chapter PDO.
 *
 * Program end-to-end di atas SQLite memori: membuat tabel, mengisi data
 * dengan exec() dan prepare(), membaca dengan query(), lalu menangani
 * kesalahan SQL dengan mode exception.
 */

$pdo = new PDO("sqlite::memory:");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);

// exec(): pembuatan tabel dan INSERT massal
$pdo->exec("CREATE TABLE tugas (id INTEGER PRIMARY KEY, judul TEXT, selesai INTEGER)");
echo "exec() menambahkan " . $pdo->exec("INSERT INTO tugas (judul, selesai) VALUES
    ('Belajar PDO', 1),
    ('Belajar SQL', 1),
    ('Belajar Transaksi', 0)") . " baris\n";

// prepare() + execute(): data dipisah dari struktur SQL
$stmt = $pdo->prepare("INSERT INTO tugas (judul, selesai) VALUES (?, ?)");
$stmt->execute(["Belajar Repository Pattern", 0]);

// query(): membaca seluruh baris
echo "Semua tugas:\n";
foreach ($pdo->query("SELECT id, judul, selesai FROM tugas ORDER BY id") as $tugas) {
    $status = $tugas["selesai"] === 1 ? "[x]" : "[ ]";
    echo "  $status {$tugas['judul']}\n";
}

// prepare() untuk SELECT dengan kondisi
$stmt = $pdo->prepare("SELECT judul FROM tugas WHERE selesai = ?");
$stmt->execute([1]);
echo "Tugas selesai: " . count($stmt->fetchAll()) . "\n";

// ERRMODE_EXCEPTION: kesalahan SQL menjadi PDOException yang bisa ditangkap
try {
    $pdo->query("SELECT tidakAda FROM tugas");
} catch (PDOException $e) {
    echo "Error tertangkap: " . $e->getMessage() . "\n";
}