Pernah nggak kamu nulis ->where('status', 'active') di sepuluh tempat berbeda dalam satu proyek Laravel? Lalu suatu hari requirement berubah jadi 'active AND verified' — dan kamu harus buka satu per satu file buat update query-nya. Menyiksa, kan?
Di seri 50 Artikel Belajar Laravel ini, artikel ke-28 akan membahas solusinya: Eloquent Scope. Fitur ini wajib kamu kuasai setelah memahami autentikasi Laravel Breeze di artikel-artikel sebelumnya. Scope adalah cara elegan Laravel untuk membungkus logika query yang berulang jadi satu method yang bisa dipanggil kapan saja dan di mana saja.
Siap bikin query kamu lebih bersih dari meja makan setelah liburan lebaran? Yuk mulai! 🚀
"Eloquent Scope adalah method di dalam Model yang membungkus kondisi query, sehingga bisa dipanggil secara berantai (chainable) dan digunakan kembali tanpa duplikasi kode."
Ada dua jenis scope: Local Scope (kondisi yang bisa dipanggil secara eksplisit) dan Global Scope (kondisi yang otomatis diterapkan di setiap query model tersebut).
🔍 Apa Itu Eloquent Scope dan Kenapa Kamu Perlu Paham Ini?
Bayangkan kamu punya warung kopi. Setiap pagi kamu selalu mengambil kopi yang sudah dimasak (status = 'ready'), stok di atas 0 (stock > 0), dan tidak kadaluarsa (expired_at > now()). Kalau kamu harus nulis tiga kondisi itu setiap kali butuh daftar kopi siap saji — ribet banget!
Nah, Scope di Eloquent itu seperti kamu punya "menu shortcut": cukup bilang ->available() dan otomatis tiga kondisi tadi langsung teraplikasi. Clean, DRY (Don't Repeat Yourself), dan mudah di-maintain.
Dalam konteks proyek Laravel dengan autentikasi Laravel Breeze, kamu pasti sudah punya model User. Di sinilah Scope paling sering dipraktikkan: menyaring user aktif, user terverifikasi, user dengan role tertentu, dan sebagainya.
Fitur Local Scope sudah ada sejak Laravel 4.x — salah satu fitur tertua yang masih eksis dan semakin disempurnakan hingga Laravel 11. Artinya, ratusan ribu proyek Laravel di seluruh dunia sudah memanfaatkan ini.
⚙️ Local Scope: Cara Praktis dengan autentikasi Laravel Breeze
Local Scope adalah scope yang kamu definisikan sendiri di model dan dipanggil secara eksplisit di query. Konvensinya: nama method di model diawali kata scope, lalu dipanggil tanpa prefix itu.
Misalnya kita pakai model User.php yang sudah ada di proyek Laravel dengan autentikasi Laravel Breeze. Buka file di app/Models/User.php.
Tulis method dengan konvensi scopeNamaScope(Builder $query) di dalam class model:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Builder; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable { // 🔑 Local Scope: hanya user aktif public function scopeActive(Builder $query): Builder { return $query->where('status', 'active'); } // 🔑 Local Scope: hanya email yang terverifikasi public function scopeVerified(Builder $query): Builder { return $query->whereNotNull('email_verified_at'); } // 🔑 Local Scope dengan parameter: filter berdasarkan role public function scopeRole(Builder $query, string $role): Builder { return $query->where('role', $role); } }
Sekarang kamu bisa merantai scope-scope tadi dengan sangat elegan:
// ✅ Dengan Scope — bersih & readable $users = User::active()->verified()->get(); // ✅ Scope dengan parameter $admins = User::active()->role('admin')->get(); // ❌ Tanpa Scope — verbose & mudah error $users = User::where('status', 'active') ->whereNotNull('email_verified_at') ->get();
Scope bisa dirantai (chained) secara bebas. User::active()->verified()->role('admin')->paginate(20) adalah query yang valid dan elegan. Urutan pemanggilan scope tidak mempengaruhi hasil.
🌐 Global Scope: Kondisi Otomatis di Semua Query
Kalau Local Scope harus dipanggil secara eksplisit, Global Scope bekerja sebaliknya — ia otomatis diterapkan di setiap query yang menggunakan model tersebut. Analoginya seperti filter Instagram yang otomatis aktif: setiap foto yang kamu upload langsung kena filter itu.
Contoh paling populer dari Global Scope di Laravel adalah SoftDeletes — setiap query otomatis mengecualikan record yang sudah dihapus (deleted_at IS NOT NULL). Kamu juga bisa membuat Global Scope sendiri!
<?php namespace App\Models\Scopes; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Scope; class ActiveScope implements Scope { public function apply(Builder $builder, Model $model): void { $builder->where('status', 'active'); } }
use App\Models\Scopes\ActiveScope; class User extends Authenticatable { // Daftarkan Global Scope di method booted() protected static function booted(): void { static::addGlobalScope(new ActiveScope); } } // Sekarang SEMUA query User otomatis filter status = 'active' $users = User::all(); // otomatis WHERE status = 'active' // Ingin bypass Global Scope? Gunakan withoutGlobalScope() $allUsers = User::withoutGlobalScope(ActiveScope::class)->all();
Global Scope yang terlalu agresif bisa menyebabkan bug tersembunyi di admin panel. Misalnya, jika kamu pasang ActiveScope di model User, maka halaman manajemen user yang seharusnya menampilkan semua user (termasuk yang nonaktif) akan gagal — kecuali kamu sadar untuk memakai withoutGlobalScope().
📊 Perbandingan & Kapan Harus Pakai Scope vs Query Biasa
Kunci menggunakan Scope dengan baik adalah tahu kapan memakainya. Berikut perbandingan lengkapnya:
| Aspek | Query Biasa | Local Scope | Global Scope |
|---|---|---|---|
| Reusability | ❌ Harus tulis ulang | ✅ Bisa dipakai ulang | ✅ Otomatis di semua query |
| Keterbacaan Kode | ⚠️ Verbose | ✅ Ekspresif & bersih | ✅ Transparan di model |
| Fleksibilitas | ✅ Bebas kapan saja | ✅ Dipanggil sesuai kebutuhan | ⚠️ Perlu bypass manual |
| Kemudahan Maintenance | ❌ Update banyak tempat | ✅ Update satu tempat | ✅ Update satu tempat |
| Cocok Untuk | Query unik/satu kali pakai | Filter yang sering dipakai | Aturan bisnis global (soft delete, tenant, dll) |
Kapan pakai Local Scope? Saat kondisi query dipakai di lebih dari 2 tempat berbeda. Nama scope harus mendeskripsikan apa yang dikembalikan, bukan bagaimana cara filternya. Contoh: gunakan scopeActive() bukan scopeWhereStatusActive().
Kapan pakai Global Scope? Untuk aturan bisnis yang tidak boleh dilupakan — seperti multi-tenancy (setiap user hanya bisa lihat data organisasinya sendiri), atau soft delete. Kalau kondisi bersifat opsional, gunakan Local Scope saja.
Di proyek Laravel dengan autentikasi Laravel Breeze, kamu bisa langsung menambahkan scopeActive() dan scopeVerified() ke model User yang sudah dihasilkan Breeze — tanpa perlu mengubah struktur apapun. Scope hidup berdampingan dengan fitur autentikasi yang ada.
🎛️ Scope Dinamis dan Contoh Nyata di Proyek Laravel
Scope tidak harus statis. Kamu bisa menambahkan parameter untuk membuat scope yang lebih fleksibel. Berikut contoh nyata di proyek e-commerce dengan autentikasi Laravel Breeze:
class Product extends Model { // Scope dengan parameter: filter harga dalam rentang tertentu public function scopePriceBetween(Builder $query, int $min, int $max): Builder { return $query->whereBetween('price', [$min, $max]); } // Scope: produk terbaru (bisa custom jumlah hari) public function scopeRecent(Builder $query, int $days = 30): Builder { return $query->where('created_at', '>=', now()->subDays($days)); } // Scope: produk berdasarkan kategori public function scopeCategory(Builder $query, string $category): Builder { return $query->where('category', $category); } } // Penggunaan — ekspresif & mudah dibaca! $products = Product::priceBetween(50000, 500000) ->recent(7) ->category('electronics') ->orderBy('price') ->paginate(12);
Query panjang seperti Product::priceBetween(50000,500000)->recent(7)->category('electronics')->paginate(12) jauh lebih mudah di-review di code review dibanding deretan ->where() yang panjang. Ini bukan sekadar estetika — ini langsung berpengaruh ke kecepatan debugging tim kamu.
No comments:
Post a Comment