BAB 16: Database
Konfigurasi koneksi database dan pengenalan lapisan database Laravel untuk berbagai driver.
Sampai bab ini, hampir semua yang kita bangun bersifat sementara. Data catatan yang dibuat di Tinker hilang saat kita restart, kebijakan authorization yang kita uji tidak tersimpan, form yang kita validasi tidak menghasilkan apa pun di storage nyata. Aplikasi yang sungguh-sungguh berguna membutuhkan tempat penyimpanan yang persisten — dan itulah database.
Di bab ini kita tidak langsung menulis query atau membuat model. Kita mulai dari sesuatu yang lebih mendasar: memahami bagaimana Laravel berbicara dengan database, apa saja pilihan yang tersedia, dan bagaimana arsitektur lapisan database ini bekerja sebelum kita menyentuh fitur-fitur di atasnya.
Lapisan Database Laravel
Laravel tidak langsung memakai PDO mentah untuk berkomunikasi dengan database. Ada beberapa lapisan yang saling menumpuk, masing-masing memberikan abstraksi lebih tinggi dari lapisan di bawahnya:
Kamu bisa memilih bekerja di lapisan mana pun tergantung kebutuhan. Raw SQL untuk kasus ekstrem yang butuh kendali penuh, Query Builder untuk sebagian besar operasi harian, dan Eloquent untuk logika domain yang kaya. Ketiga lapisan akan kita pelajari di bab-bab berikutnya — tapi semuanya bergantung pada konfigurasi koneksi yang kita bahas sekarang.
Database yang Didukung
Laravel 12 mendukung lima sistem database secara resmi:
| Driver | Versi Minimum | Keterangan |
|---|---|---|
| MariaDB | 10.3+ | Fork MySQL yang kompatibel penuh |
| MySQL | 5.7+ | Database relasional paling populer |
| PostgreSQL | 10.0+ | Fitur paling lengkap, unggul untuk data kompleks |
| SQLite | 3.26.0+ | File-based, sempurna untuk development dan testing |
| SQL Server | 2017+ | Untuk ekosistem Microsoft |
Untuk kebanyakan aplikasi web, MySQL atau PostgreSQL adalah pilihan utama di production. SQLite menjadi default di instalasi fresh Laravel karena tidak memerlukan server terpisah — kita hanya perlu sebuah file.
Konfigurasi di config/database.php
Semua pengaturan database terpusat di satu file: config/database.php. File ini mendefinisikan koneksi-koneksi yang tersedia dan mana yang dipakai secara default.
Struktur dasarnya seperti ini:
<?php
// config/database.php
return [
'default' => env('DB_CONNECTION', 'sqlite'),
'connections' => [
'sqlite' => [
'driver' => 'sqlite',
'url' => env('DB_URL'),
'database' => env('DB_DATABASE', database_path('database.sqlite')),
'prefix' => '',
],
'mysql' => [
'driver' => 'mysql',
'url' => env('DB_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'laravel'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'charset' => env('DB_CHARSET', 'utf8mb4'),
'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
'prefix' => '',
'strict' => true,
'engine' => null,
],
'pgsql' => [
'driver' => 'pgsql',
'url' => env('DB_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '5432'),
'database' => env('DB_DATABASE', 'laravel'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'prefix' => '',
'schema' => 'public',
],
],
'migrations' => [
'table' => 'migrations',
'update_date_on_publish' => true,
],
];
Perhatikan pola env('DB_HOST', '127.0.0.1') — nilai aktual dibaca dari environment variable, bukan di-hardcode di sini. Nilai kedua adalah default yang dipakai kalau environment variable tidak tersedia.
File .env sebagai Sumber Konfigurasi
Nilai-nilai yang dibaca config/database.php berasal dari file .env di root proyek. Untuk koneksi MySQL, bagian yang relevan terlihat seperti ini:
# .env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=nama_database_kamu
DB_USERNAME=root
DB_PASSWORD=
File .env tidak pernah masuk ke version control (sudah terdaftar di .gitignore) karena menyimpan kredensial yang berbeda untuk tiap environment: laptop developer, staging server, dan production server masing-masing punya .env sendiri.
Jangan pernah commit file .env ke repository. Jika ada data sensitif seperti password database atau API key yang ter-commit, segera rotasi kredensial tersebut — menghapus file dari history git tidak cukup jika repository sudah di-push.
Laravel menyediakan .env.example sebagai template. Setiap kali menambahkan environment variable baru, tambahkan juga ke .env.example dengan nilai kosong atau placeholder agar developer lain tahu variable apa yang dibutuhkan.
Beralih ke MySQL
Instalasi fresh Laravel menggunakan SQLite sebagai default. Untuk beralih ke MySQL, cukup ubah file .env:
# .env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=belajar_laravel
DB_USERNAME=root
DB_PASSWORD=
Setelah mengubah .env, jalankan:
php artisan config:clear
Ini membersihkan cache konfigurasi agar Laravel membaca nilai .env yang baru.
Database URL — Alternatif untuk Hosting Modern
Beberapa platform hosting seperti Heroku, Railway, atau Supabase menyediakan koneksi database dalam bentuk satu URL tunggal, bukan variabel-variabel terpisah. Laravel mendukung format ini via DB_URL:
# .env
DB_URL=mysql://root:password@127.0.0.1:3306/belajar_laravel?charset=utf8mb4
Laravel akan mengurai URL tersebut dan mengekstrak host, port, database, username, dan password secara otomatis. Ketika DB_URL tersedia, nilainya mengoverride variabel DB_HOST, DB_PORT, dll.
Menguji Koneksi dengan Artisan
Setelah mengatur konfigurasi, kita bisa memverifikasi koneksi berhasil menggunakan perintah Artisan yang kita pelajari di bab sebelumnya:
php artisan db:show
Output menampilkan ringkasan koneksi aktif: nama driver, host, database, versi server, dan jumlah tabel. Kalau koneksi gagal, kamu akan mendapat error yang informatif tentang apa yang salah.
Untuk melihat detail tabel tertentu setelah database memiliki isi:
php artisan db:table users
Raw Query dengan DB Facade
Meskipun di bab-bab selanjutnya kita akan lebih sering pakai Query Builder dan Eloquent, ada kalanya raw SQL lebih praktis. Laravel menyediakan DB facade untuk keperluan ini.
Select
<?php
// Di controller atau di mana pun
use Illuminate\Support\Facades\DB;
// Query dasar — mengembalikan array of stdClass objects
$catatan = DB::select('SELECT * FROM catatan WHERE user_id = ?', [1]);
foreach ($catatan as $item) {
echo $item->judul;
}
// Named binding — lebih ekspresif untuk query yang panjang
$catatan = DB::select(
'SELECT * FROM catatan WHERE user_id = :uid AND prioritas = :prioritas',
['uid' => 1, 'prioritas' => 'tinggi']
);
// Mengambil nilai tunggal (scalar)
$total = DB::scalar('SELECT COUNT(*) FROM catatan WHERE user_id = ?', [1]);
Insert, Update, Delete
// Insert
DB::insert(
'INSERT INTO catatan (judul, isi, user_id) VALUES (?, ?, ?)',
['Judul Catatan', 'Isi catatan di sini', 1]
);
// Update — mengembalikan jumlah baris yang terpengaruh
$terpengaruh = DB::update(
'UPDATE catatan SET prioritas = ? WHERE user_id = ?',
['rendah', 1]
);
// Delete — juga mengembalikan jumlah baris
$terhapus = DB::delete('DELETE FROM catatan WHERE created_at < ?', ['2024-01-01']);
Statement Umum
Untuk DDL atau perintah yang tidak mengembalikan hasil:
DB::statement('CREATE INDEX idx_prioritas ON catatan (prioritas)');
Raw query menggunakan parameter binding (? atau :nama) secara otomatis melindungi dari SQL injection. Yang tidak aman adalah DB::unprepared() yang mengeksekusi string SQL langsung — hindari menggunakannya dengan input dari user.
Transaksi Database
Ketika beberapa operasi harus berhasil atau gagal bersama-sama, gunakan transaksi. Laravel menyediakan cara paling sederhana melalui closure:
use Illuminate\Support\Facades\DB;
DB::transaction(function () {
DB::update('UPDATE akun SET saldo = saldo - 500000 WHERE id = ?', [1]);
DB::update('UPDATE akun SET saldo = saldo + 500000 WHERE id = ?', [2]);
});
Jika ada exception di dalam closure, transaksi di-rollback otomatis. Jika selesai tanpa exception, di-commit otomatis. Tidak perlu menulis beginTransaction(), commit(), atau rollBack() secara manual untuk kasus ini.
Untuk situasi yang butuh kontrol lebih eksplisit:
DB::beginTransaction();
try {
// serangkaian operasi
DB::commit();
} catch (\Throwable $e) {
DB::rollBack();
throw $e;
}
Query Logging untuk Debug
Saat debugging, terkadang kita perlu tahu query SQL apa yang sebenarnya dijalankan Laravel. DB::listen() memungkinkan kita “mendengarkan” setiap query yang dieksekusi:
<?php
// app/Providers/AppServiceProvider.php
use Illuminate\Database\Events\QueryExecuted;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
public function boot(): void
{
if (app()->environment('local')) {
DB::listen(function (QueryExecuted $query) {
Log::debug('Query', [
'sql' => $query->toRawSql(),
'waktu' => $query->time . 'ms',
]);
});
}
}
$query->toRawSql() menghasilkan SQL dengan binding yang sudah diisi — lebih mudah dibaca untuk debugging daripada SQL dengan tanda tanya.
Latihan
Coba kerjakan eksplorasi berikut:
-
Inspeksi koneksi — Buka
config/database.phpdi proyek kamu. Cermati perbedaan konfigurasi antara drivermysqldanpgsql. Variabel apa yang sama dan apa yang berbeda? -
Uji koneksi — Jalankan
php artisan db:showdi proyek kamu. Jika terjadi error, perbaiki konfigurasi di.envsampai koneksi berhasil. -
Eksperimen raw query — Buka Tinker dengan
php artisan tinker, lalu jalankanDB::select('SELECT 1+1 as hasil'). Apa yang dikembalikan? Coba jugaDB::scalar('SELECT NOW()').
Penutup Bab
Konfigurasi database bukan sekadar mengisi username dan password. Ini adalah fondasi yang menentukan bagaimana seluruh lapisan database di atasnya bisa bekerja — dari raw query sederhana hingga relasi Eloquent yang kompleks.
Dengan koneksi yang sudah terjalin, langkah logis berikutnya adalah mengelola struktur database itu sendiri. Bagaimana cara membuat tabel, mengubah kolom, atau menghapus index tanpa harus menulis SQL ALTER TABLE secara manual setiap kali ada perubahan? Itulah yang akan kita pelajari di bab berikutnya melalui sistem migrasi Laravel.