Eloquent ORM Laravel 11: Relasi, Eager Loading & Contoh

Developer yang baru pindah dari PHP native ke Laravel sering merasa Eloquent terlalu "ajaib": satu baris kode bisa menjalankan banyak query tanpa terlihat. Akibatnya halaman daftar...

Eloquent ORM Laravel 11: Relasi, Eager Loading & Contoh

Developer yang baru pindah dari PHP native ke Laravel sering merasa Eloquent terlalu "ajaib": satu baris kode bisa menjalankan banyak query tanpa terlihat. Akibatnya halaman daftar pesanan yang tadinya cepat tiba-tiba butuh tiga detik setelah data mencapai ribuan baris. Masalahnya jarang ada di Eloquent, melainkan di cara memakainya.

Artikel ini menjelaskan Eloquent ORM di Laravel 11 dengan contoh studi kasus toko sederhana (pelanggan, pesanan, produk): cara kerja model, operasi CRUD, relasi, eager loading, accessor, scope, soft delete, serta batas di mana Query Builder atau SQL mentah lebih tepat.

1. Apa Itu ORM dan Bagaimana Eloquent Bekerja

ORM (Object-Relational Mapping) memetakan baris tabel menjadi objek PHP. Eloquent memakai pola Active Record: satu objek model mewakili satu baris, dan objek itu tahu cara menyimpan dirinya sendiri. Di balik layar, setiap pemanggilan method seperti where() atau get() diterjemahkan ke SQL lewat Query Builder, lalu dijalankan memakai PDO dengan parameter binding sehingga aman dari SQL injection selama Anda tidak menyisipkan input mentah ke whereRaw().

2. Model dan Konvensi

php artisan make:model Order -mf

Opsi -m membuat migration dan -f membuat factory. Berdasarkan konvensi, model Order dipetakan ke tabel orders, primary key id, dan kolom created_at/updated_at diisi otomatis.

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Order extends Model
{
    use SoftDeletes;

    protected $fillable = ['customer_id', 'invoice_no', 'status', 'total', 'paid_at'];

    protected function casts(): array
    {
        return [
            'total'   => 'decimal:2',
            'paid_at' => 'datetime',
            'meta'    => 'array',
        ];
    }
}

Di Laravel 11, casts ditulis sebagai method casts(). Cast datetime mengubah kolom menjadi objek Carbon, sehingga Anda bisa menulis $order->paid_at->format('d M Y'). Properti $fillable membatasi kolom yang boleh diisi massal, melindungi dari pengguna yang menyisipkan field seperti status=paid lewat form.

3. CRUD dengan Eloquent

// Read
$order  = Order::findOrFail($id);                 // 404 otomatis jika tidak ada
$recent = Order::where('status', 'paid')
    ->latest()
    ->limit(20)
    ->get();

// Create
$order = Order::create([
    'customer_id' => $customer->id,
    'invoice_no'  => 'INV-2026-0001',
    'status'      => 'pending',
    'total'       => 150000,
]);

// Update
$order->update(['status' => 'paid', 'paid_at' => now()]);

// Upsert sederhana
Customer::updateOrCreate(
    ['phone' => '628123456789'],
    ['name' => 'Budi']
);

// Delete (soft delete karena model memakai SoftDeletes)
$order->delete();

Perbedaan yang perlu diingat: get() mengembalikan Collection, first() mengembalikan satu model atau null, dan findOrFail() melempar exception yang otomatis menjadi halaman 404.

4. Relasi Antar Model

RelasiContohMethod
One to manySatu pelanggan punya banyak pesananhasMany / belongsTo
One to oneSatu user punya satu profilhasOne / belongsTo
Many to manyPesanan berisi banyak produk, produk ada di banyak pesananbelongsToMany + tabel pivot
Has many throughPelanggan ke item pesanan lewat pesananhasManyThrough
// Customer.php
public function orders(): HasMany
{
    return $this->hasMany(Order::class);
}

// Order.php
public function customer(): BelongsTo
{
    return $this->belongsTo(Customer::class);
}

public function products(): BelongsToMany
{
    return $this->belongsToMany(Product::class, 'order_items')
        ->withPivot(['qty', 'price'])
        ->withTimestamps();
}

Kolom price di tabel pivot sengaja disimpan: harga produk bisa berubah, tetapi nilai pesanan lama harus tetap sesuai harga saat transaksi.

5. Masalah N+1 dan Eager Loading

Ini penyebab paling umum aplikasi Laravel melambat. Perhatikan kode berikut:

$orders = Order::latest()->take(50)->get();

foreach ($orders as $order) {
    echo $order->customer->name;   // 1 query per pesanan
}

Hasilnya 1 query untuk pesanan ditambah 50 query untuk pelanggan, total 51 query. Dengan eager loading jumlahnya turun menjadi 2:

$orders = Order::with(['customer:id,name', 'products'])
    ->withCount('products')
    ->latest()
    ->take(50)
    ->get();

Agar kesalahan ini ketahuan sejak development, aktifkan proteksi lazy loading di AppServiceProvider:

use Illuminate\Database\Eloquent\Model;

public function boot(): void
{
    Model::preventLazyLoading(! app()->isProduction());
}

Dengan ini, setiap akses relasi yang belum di-load akan melempar exception di lingkungan lokal. Pembahasan lebih dalam ada di optimasi performa query Eloquent.

6. Accessor, Mutator, dan Scope

Accessor dan mutator di Laravel 11 memakai kelas Attribute:

use Illuminate\Database\Eloquent\Casts\Attribute;

protected function invoiceNo(): Attribute
{
    return Attribute::make(
        set: fn (string $value) => strtoupper(trim($value)),
    );
}

protected function totalFormatted(): Attribute
{
    return Attribute::make(
        get: fn () => 'Rp ' . number_format($this->total, 0, ',', '.'),
    );
}

Local scope membungkus kondisi yang sering dipakai agar tidak ditulis ulang:

public function scopePaid($query)
{
    return $query->where('status', 'paid');
}

public function scopeBetween($query, $from, $to)
{
    return $query->whereBetween('created_at', [$from, $to]);
}

// pemakaian
$omzet = Order::paid()->between(now()->startOfMonth(), now())->sum('total');

7. Mengolah Data Besar

Memanggil Order::all() pada tabel berisi ratusan ribu baris akan menghabiskan memori. Gunakan pemrosesan bertahap:

  • chunkById(500, fn ($rows) => ...) untuk memproses per 500 baris dengan aman walau ada update.
  • lazy() untuk iterasi satu per satu dengan memori rendah.
  • ->select([...]) untuk mengambil kolom yang dibutuhkan saja.
  • paginate(20) di halaman daftar, jangan get().

8. Kapan Tidak Memakai Eloquent

Eloquent unggul untuk CRUD dan logika per record. Untuk laporan agregat besar (misalnya omzet per kategori per bulan dari jutaan baris), Query Builder atau SQL dengan DB::select() sering lebih cepat dan lebih mudah dibaca, karena Anda tidak perlu membuat objek model untuk setiap baris. Mencampur keduanya dalam satu proyek adalah hal yang wajar.

9. Kesalahan Umum dan Solusinya

  • MassAssignmentException: kolom belum ada di $fillable. Tambahkan, jangan mematikan proteksi dengan $guarded = [] di model yang menerima input pengguna.
  • "Call to a member function on null": relasi belongsTo kosong. Pakai $order->customer?->name atau withDefault().
  • Data yang dihapus masih muncul atau hilang tak terduga: lupa bahwa SoftDeletes menyaring otomatis. Gunakan withTrashed() bila perlu.
  • Total uang meleset: menyimpan uang sebagai float. Gunakan kolom DECIMAL dan cast decimal:2, atau simpan dalam satuan terkecil sebagai integer.

Checklist Model yang Sehat

  • $fillable berisi hanya kolom yang memang diisi dari form.
  • Kolom tanggal, boolean, JSON, dan uang punya cast yang tepat.
  • Semua relasi diberi return type (HasMany, BelongsTo).
  • Halaman daftar memakai with() dan paginate().
  • preventLazyLoading aktif di lingkungan development.
  • Kolom yang sering difilter (status, customer_id) punya index di migration.

Sebelum membuat model, rancang tabelnya dulu; panduan merancang ERD sederhana membantu menentukan relasi yang tepat, dan migrasi database yang aman membahas cara mengubah struktur tabel tanpa kehilangan data.

Ingin langsung mempraktikkannya?

Buat akun gratis untuk mengunduh contoh aplikasi Laravel, menyimpan tutorial favorit, dan mencoba kursus. Gratis, bisa daftar dengan akun Google.

Daftar Gratis Lihat katalog aplikasi
Yudhi
Ditulis oleh
Yudhi
Founder & Lead Developer, GudangCode

Yudhi adalah founder GudangCode dan developer Laravel dengan pengalaman membangun puluhan sistem informasi bisnis siap pakai — mulai dari POS, HRIS, hingga aplikasi manajemen. Ia menulis panduan dan artikel di GudangCode untuk membantu developer Indonesia menjalankan, memahami, dan men-deploy source code Laravel dengan benar.

Laravel PHP MySQL Sistem Informasi Bisnis
Lihat semua artikel Yudhi
Eloquent Orm Laravel Programming & Laravel
Bacaan Paling Dicari
📚 Free Learning Hub

Learn Coding for Free at DhieCoderWeb

Explore Laravel, PHP, JavaScript tutorials, source code, web development guides, and practical programming tips.

DhieCoderWeb
100+
Tutorials
Free
Learning
SEO
Tips
Visit Dhiecoderweb.com →