Belajar PHP - Working with Databases (PDO & Eloquent)
Series/Belajar PHP/Episode 11
Episode 11 of 23

Belajar PHP - Working with Databases (PDO & Eloquent)

Menghubungkan PHP dengan database: koneksi PDO ke MySQL/PostgreSQL/SQLite, prepared statements sebagai tameng SQL injection, transaksi ACID untuk konsistensi data, lalu ORM Eloquent dan Doctrine serta query builder dan relationships untuk produktivitas pengembangan.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Sampai sini, semua data kita hidup dalam memori dan hilang saat request selesai. Episode 11 mengubah itu: kalian akan menyimpan, membaca, dan mengubah data secara persisten lewat database. Ini adalah momen "aplikasi kalian menjadi nyata" — dari skrip latihan menjadi sistem yang melayani pengguna sungguhan.

Mengapa database penting? Hampir semua aplikasi bisnis — toko online, CMS, dashboard — adalah "antarmuka di atas data". Cara kalian mengakses data menentukan keamanan (SQL injection), konsistensi (transaksi), dan performa (query). Kita mulai dengan PDO yang aman, lalu ke Eloquent sebagai ORM yang kalian temui di episode 13+.

PDO: Koneksi ke MySQL/PostgreSQL/SQLite

PDO (PHP Data Objects) adalah satu API untuk banyak database. Koneksi:

Koneksi PDO
<?php
declare(strict_types=1);
 
$dsn = "mysql:host=127.0.0.1;port=3306;dbname=belajar_php;charset=utf8mb4";
$user = "app";
$pass = getenv("DB_PASS") ?: "local-secret";
 
$pdo = new PDO($dsn, $user, $pass, [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES   => false,
]);

Tiga atribut di atas adalah standar produksi:

  • ERRMODE_EXCEPTION — error jadi exception yang bisa ditangkap (episode 8).
  • FETCH_ASSOC — hasil query berupa array asosiatif.
  • EMULATE_PREPARES => false — pakai prepared statement sungguhan di sisi database (kunci keamanan).

Ganti DSN untuk database lain:

DatabaseDSN
MySQLmysql:host=127.0.0.1;dbname=app;charset=utf8mb4
PostgreSQLpgsql:host=127.0.0.1;dbname=app
SQLitesqlite:/path/ke/file.db

Jangan pernah menaruh kredensial di kode — baca dari environment variable (detail di episode 19).

Prepared Statements: Tameng SQL Injection

Inilah aturan nomor satu keamanan database. JANGAN pernah menyusun query dengan concatenation:

Bahaya: SQL injection
// ⛔ JANGAN PERNAH
$id = $_GET["id"];
$pdo->query("SELECT * FROM users WHERE id = $id");

Jika $id berisi 1 OR 1=1, seluruh tabel bocor. Yang benar — prepared statement:

Aman: prepared statement
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ? AND aktif = ?");
$stmt->execute([$id, 1]);
$user = $stmt->fetch();
 
// atau named placeholder untuk kejelasan
$stmt = $pdo->prepare(
    "SELECT * FROM users WHERE email = :email AND aktif = :aktif"
);
$stmt->execute([":email" => $email, ":aktif" => true]);

Database menerima nilai dan query secara terpisah — input pengguna tidak pernah bisa menjadi bagian dari struktur SQL. Ini tameng utama terhadap kelas serangan yang kita dalami di episode 17.

CRUD dan Fetching

CRUD dengan PDO
// INSERT dengan last insert id
$stmt = $pdo->prepare(
    "INSERT INTO users (nama, email) VALUES (:nama, :email)"
);
$stmt->execute([":nama" => $nama, ":email" => $email]);
$id = (int) $pdo->lastInsertId();
 
// SELECT semua baris
$rows = $pdo
    ->query("SELECT * FROM users ORDER BY id DESC")
    ->fetchAll();
 
// UPDATE
$pdo->prepare("UPDATE users SET nama = :nama WHERE id = :id")
    ->execute([":nama" => $namaBaru, ":id" => $id]);
 
// DELETE
$pdo->prepare("DELETE FROM users WHERE id = :id")
    ->execute([":id" => $id]);

fetch() mengambil satu baris, fetchAll() semua baris. Untuk jumlah besar, pertimbangkan streaming dengan yield (episode 20).

Transaksi: Konsistensi Data

Transaksi menjamin sekelompok operasi semua-atau-tidak-sama:

Transaksi transfer
$pdo->beginTransaction();
try {
    $pdo->prepare("UPDATE akun SET saldo = saldo - ? WHERE id = ?")
        ->execute([100000, $dari]);
    $pdo->prepare("UPDATE akun SET saldo = saldo + ? WHERE id = ?")
        ->execute([100000, $ke]);
 
    $pdo->commit();
} catch (Throwable $e) {
    $pdo->rollBack();
    throw $e;
}

Tanpa transaksi, jika UPDATE kedua gagal, uang hilang dari akun $dari tapi tidak masuk ke akun $ke. Transaksi (ACID) mencegah state korup seperti ini. Ingat pola tetap: begin → operasi → commit, dan rollBack di catch.

Tip

Rule of thumb: setiap operasi yang mengubah lebih dari satu baris atau baris yang saling bergantung (transfer, order + stok, payment + status) layak dibungkus transaksi. Operasi tunggal yang sederhana tidak perlu.

ORM: Eloquent dan Doctrine

Menulis SQL manual di setiap tempat cepat lelah dan rawan error. ORM memetakan tabel menjadi class dan baris menjadi object:

  • Eloquent (Laravel) — aktif record, mudah, sangat produktif, di-episode 13+.
  • Doctrine (Symfony) — data mapper, lebih eksplisit, cocok untuk domain yang kompleks.

Contoh model Eloquent:

Eloquent model + relationships
use Illuminate\Database\Eloquent\Model;
 
class User extends Model
{
    protected $fillable = ["nama", "email"];
 
    public function orders()
    {
        return $this->hasMany(Order::class);
    }
}

Query builder / Eloquent memakai data yang sama dan tetap aman dari injection karena memakai binding di belakang layar:

Query builder (pola sama di Eloquent & Laravel)
$users = User::query()
    ->where("aktif", true)
    ->where("usia", ">=", 18)
    ->orderBy("nama")
    ->limit(10)
    ->get();
 
$totalBelanja = User::query()
    ->findOrFail($id)
    ->orders()
    ->sum("total");

Kapan memilih mana:

PendekatanKelebihanDipakai kapan
PDO langsungTransparan, kontrol penuh, tanpa overheadSkrip sederhana, performance-critical, non-framework
EloquentProduktif, hubungan antar-model intuitifAplikasi Laravel (default)
DoctrineIsolasi domain kuat, mapping eksplisitAplikasi Symfony kompleks

Warning

ORM bukan pengganti pemahaman SQL — malah sebaliknya. Kapan kalian tidak paham SQL, kalian tidak akan tahu kenapa Eloquent menghasilkan query aneh atau N+1 (episode 18). Kuasai SQL dan PDO dulu, ORM menjadi alat yang mempercepat, bukan kotak hitam.

Common Pitfalls

  • Concatenation di query — pintu SQL injection; wajib prepared statements.
  • utf8 alih-alih utf8mb4 di MySQL — emoji dan karakter 4-byte rusak; selalu utf8mb4.
  • Lupa transaksi untuk multi-write — data korup saat salah satu langkah gagal.
  • Menyimpan password plaintext — jangan; pakai password_hash (episode 14).
  • Mengembalikan seluruh baris untuk satu kebutuhanSELECT * terus-menerus boros; pilih kolom yang dibutuhkan.

Penutup

Inti yang harus dibawa pulang:

  • PDO adalah satu API untuk MySQL/PostgreSQL/SQLite — konfigurasi ERRMODE_EXCEPTION + FETCH_ASSOC + EMULATE_PREPARES=false.
  • Prepared statements adalah tameng utama SQL injection — jangan pernah concatenate input.
  • Transaksi menjamin konsistensi untuk operasi multi-write.
  • Eloquent (Laravel) dan Doctrine (Symfony) adalah ORM yang mempercepat pengembangan — pahami SQL-nya lebih dulu.

Di episode 12 selanjutnya kita menjembatani PHP dengan pengguna: Form Handling & HTTP Basics$_GET/$_POST, validasi input, upload file, lalu headers, redirect, JSON response, cookies, dan session dengan secure flags. Di sini PHP menunjukkan keunggulan utamanya sebagai bahasa web.

Belajar PHP - Working with Databases (PDO & Eloquent) | Belajar PHP