Database dengan PDO

Koneksi PDO, prepared statement, fetch mode, transaksi, penanganan error, dan pola repository sederhana.

Part 17 dari 22 ± 55 menit Gratis

Apa itu PDO?

PDO (PHP Data Objects) adalah antarmuka tunggal untuk mengakses banyak database (MySQL/MariaDB, PostgreSQL, SQLite, SQL Server). Cukup ganti DSN-nya, kode query tetap sama. Jangan pakai ekstensi mysql_* lama (sudah dihapus); pakai PDO atau MySQLi.

Koneksi

$dsn = 'mysql:host=127.0.0.1;dbname=toko;charset=utf8mb4';

$pdo = new PDO($dsn, 'user', 'rahasia', [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,   // error jadi PDOException
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,         // hasil berupa array asosiatif
    PDO::ATTR_EMULATE_PREPARES   => false,                    // prepared statement sungguhan
]);

// untuk belajar tanpa server: SQLite
$pdo = new PDO('sqlite:' . __DIR__ . '/toko.sqlite');

Simpan kredensial di .env/konfigurasi, bukan di kode. Tiga opsi di atas adalah standar yang direkomendasikan.

Prepared statement — pertahanan SQL Injection

// SALAH — rawan injeksi
$pdo->query("SELECT * FROM users WHERE email = '$email'");

// BENAR — data dipisah dari perintah SQL
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email AND aktif = :aktif');
$stmt->execute(['email' => $email, 'aktif' => 1]);
$user = $stmt->fetch();            // satu baris, atau false

$semua = $pdo->prepare('SELECT id, nama FROM produk WHERE harga <= ?');
$semua->execute([100000]);
foreach ($semua->fetchAll() as $p) { echo $p['nama']; }

Dengan placeholder (:nama atau ?), database menerima SQL dan data secara terpisah sehingga data tidak pernah ditafsirkan sebagai perintah. Hanya nilai yang boleh jadi placeholder — nama tabel/kolom tidak bisa. Untuk ORDER BY dinamis, cocokkan dengan daftar putih.

CRUD

// Create
$pdo->prepare('INSERT INTO produk (nama, harga) VALUES (?, ?)')->execute(['Mouse', 75000]);
$idBaru = (int) $pdo->lastInsertId();

// Update
$st = $pdo->prepare('UPDATE produk SET harga = :harga WHERE id = :id');
$st->execute(['harga' => 80000, 'id' => $idBaru]);
echo $st->rowCount();               // baris yang terpengaruh

// Delete
$pdo->prepare('DELETE FROM produk WHERE id = ?')->execute([$idBaru]);

Fetch mode

ModeHasil
PDO::FETCH_ASSOC['nama' => 'Mouse']
PDO::FETCH_OBJObjek anonim: $row->nama
PDO::FETCH_CLASSLangsung jadi objek class milikmu (cocok dengan constructor promotion/DTO)
PDO::FETCH_COLUMNSatu kolom saja → daftar nilai
PDO::FETCH_KEY_PAIR[id => nama]

Transaksi — semua berhasil atau tidak sama sekali

try {
    $pdo->beginTransaction();

    $pdo->prepare('UPDATE rekening SET saldo = saldo - ? WHERE id = ?')->execute([50000, 1]);
    $pdo->prepare('UPDATE rekening SET saldo = saldo + ? WHERE id = ?')->execute([50000, 2]);

    $pdo->commit();
} catch (Throwable $e) {
    if ($pdo->inTransaction()) {
        $pdo->rollBack();
    }
    throw $e;
}

Tanpa transaksi, jika proses mati di tengah, uang bisa hilang dari rekening 1 tapi belum masuk rekening 2. Transaksi menjamin sifat ACID. Gunakan untuk setiap rangkaian perubahan yang harus konsisten (membuat pesanan + mengurangi stok).

Pola Repository

Jangan menyebar SQL ke seluruh aplikasi. Kumpulkan akses data di class khusus:

final class ProdukRepository
{
    public function __construct(private PDO $pdo) {}

    /** @return Produk[] */
    public function semua(): array
    {
        return $this->pdo->query('SELECT id, nama, harga FROM produk ORDER BY nama')
                         ->fetchAll(PDO::FETCH_CLASS, Produk::class);
    }

    public function cari(int $id): ?Produk
    {
        $st = $this->pdo->prepare('SELECT id, nama, harga FROM produk WHERE id = ?');
        $st->execute([$id]);

        return $st->fetchObject(Produk::class) ?: null;
    }
}

Keuntungannya: SQL terpusat, mudah diganti/diuji, dan bagian lain aplikasi cukup memanggil $repo->cari(5). Framework seperti Laravel membungkus pola ini dalam ORM (Eloquent), tetapi memahami PDO membuatmu mengerti apa yang terjadi di baliknya.

Praktik baik

  • Satu koneksi per request (jangan buat PDO berulang).
  • Pilih kolom yang dibutuhkan, hindari SELECT * di kode produksi.
  • Tambahkan indeks pada kolom yang sering di-WHERE; waspadai query N+1 di dalam loop.
  • Gunakan migrasi (Phinx, Laravel migration) untuk mengelola skema.

Praktikkan langsung, gratis

Tulis dan jalankan kode langsung di browser, dapatkan penilaian otomatis. Daftar gratis kapan saja untuk menyimpan progres dan meraih sertifikat.

Coba latihan sekarang, tanpa daftar
Bagikan: WhatsApp Facebook LinkedIn X