Query Builder: SQL Tanpa Tulis SQL
Pakai DB::table untuk query fleksibel tanpa model.
Kapan Eloquent Terlalu Berat?
Eloquent itu nyaman, tapi setiap baris hasil dibungkus menjadi objek model lengkap dengan relasi, casting, dan event. Untuk laporan yang menarik puluhan ribu baris, itu boros memori dan lambat. Query builder memberi kekuatan SQL mentah yang dibungkus API fluent: ringan, cepat, tanpa fitur model. Kenapa dua-duanya ada? Karena kebutuhan baca data laporan dan kebutuhan logika bisnis itu beda sifat.
Query Dasar
// app/Http/Controllers/LaporanController.php
use Illuminate\Support\Facades\DB;
$users = DB::table('users')
->where('active', true)
->where('created_at', '>=', now()->subMonth())
->orderBy('name')
->get(); // Collection berisi stdClassHasilnya adalah stdClass biasa, bukan model. Kamu akses dengan $user->name, tapi tidak ada relasi, tidak ada casting tanggal otomatis, tidak ada accessor. Semua method where, orderBy, limit, dan join bekerja sama seperti di Eloquent karena keduanya memakai query builder yang sama di bawahnya.
Join Antar Tabel
// app/Http/Controllers/LaporanController.php
$posts = DB::table('posts')
->join('users', 'posts.user_id', '=', 'users.id')
->leftJoin('categories', 'posts.category_id', '=', 'categories.id')
->select('posts.id', 'posts.title', 'users.name as penulis', 'categories.name as kategori')
->get();join() untuk inner join, leftJoin() untuk left join. Selalu beri alias pada kolom yang namanya kembar (users.name vs categories.name) supaya tidak saling menimpa di objek hasil.
Jebakan umum: lupa menulis select() saat join. Kolom id dari semua tabel akan bertumpuk dan yang terbaca hanya id dari tabel terakhir di-join. Biasakan selalu menulis select() eksplisit setiap kali ada join, itu menyelamatkanmu dari bug yang sulit dilacak.
Insert, Update, Delete Mentah
// database/seeders/PostSeeder.php
DB::table('posts')->insert([
'title' => 'Halo Dunia',
'body' => 'Isi artikel...',
'created_at' => now(),
'updated_at' => now(),
]);
DB::table('posts')->where('id', 1)->update(['title' => 'Judul Baru']);
DB::table('posts')->where('spam', true)->delete();Tidak ada $fillable di sini, tidak ada timestamps otomatis, tidak ada validasi. Kamu yang mengisi created_at dan updated_at secara manual. Itu harga yang dibayar untuk kecepatan dan kontrol penuh.
Catatan teknis: Aturan memilihnya sederhana dan layak dihafal. Logika domain (aturan bisnis, relasi antar model, event, casting): pakai Eloquent. Laporan, ekspor CSV/Excel, agregasi, atau operasi massal puluhan ribu baris: pakai query builder. Banyak aplikasi memakai keduanya berdampingan di controller yang berbeda, dan itu normal. Jangan memaksakan Eloquent untuk laporan berat, dan jangan memakai query builder mentah untuk logika bisnis yang butuh validasi model.
Memproses Data Besar dengan chunk()
Jangan pernah memanggil get() untuk ratusan ribu baris sekaligus. Pakai chunk() untuk memproses per batch kecil:
// app/Console/Commands/HitungUlangViews.php
DB::table('posts')->orderBy('id')->chunk(1000, function ($posts) {
foreach ($posts as $post) {
// proses 1000 baris, lalu lanjut ke batch berikutnya
}
});chunk() mengambil 1000 baris, menjalankan callback, lalu mengambil 1000 berikutnya. Memori tetap stabil berapa pun total barisnya. Untuk update massal sederhana, where()->update() tetap lebih cepat karena hanya satu query; chunk() dipakai untuk logika per baris yang tidak bisa diungkapkan dalam SQL.
Tantangan
Laporan Penulis
Via tinker dengan query builder: tampilkan daftar penulis (users.name) beserta jumlah post masing-masing (posts.count), urut terbanyak. Gunakan join + groupBy.