Day 5 — Eloquent ORM
Tabel kelas dan siswa dari Day 4 sudah siap. Sekarang kita akan mengelola isi tabel itu melalui Model Laravel—membaca, menambah, mengubah, dan menghapus data tanpa menulis SQL manual untuk setiap pekerjaan.
Target Belajar Hari Ini
Pada Day 4 kita membuat struktur database lewat migration: tabel kelas, tabel siswa, kolom, dan foreign key. Hari ini kita tidak mengubah bentuk tabel tersebut. Kita mulai mengisi dan membaca data nyata melalui Eloquent ORM.
Bayangkan database sebagai lemari arsip sekolah. Migration merakit lemari dan memberi label pada setiap laci; Eloquent adalah petugas arsip yang memahami permintaan PHP lalu mengambil atau menyimpan berkas ke laci yang tepat.
Setelah menyelesaikan materi ini, kamu ditargetkan mampu:
- Memahami apa itu Eloquent dan peran Model sebagai penghubung kode PHP dengan tabel database.
- Melakukan CRUD—Create, Read, Update, Delete—melalui Model tanpa menulis SQL manual.
- Memilih antara
all(),find(),where(), danfirst()sesuai kebutuhan. - Memahami mass assignment serta memakai
$fillableuntuk membatasi field yang boleh diisi secara massal.
Empat tindakan berikut adalah peta sederhana untuk hari ini. Setiap tindakan memakai class Model Siswa, sementara Eloquent menerjemahkannya menjadi query database.
// Create — menambah satu siswa
$siswa = Siswa::create(['nama' => 'Alya', 'kelas_id' => 1]);
// Read — mengambil siswa dengan primary key 1
$siswa = Siswa::find(1);
// Update — mengubah data yang sudah diambil
$siswa->update(['nama' => 'Alya Putri']);
// Delete — menghapus baris tersebut
$siswa->delete();
siswas belum dibuat, perintah CRUD akan gagal. Perbaiki dengan memastikan koneksi database benar dan menjalankan php artisan migrate:status lalu php artisan migrate.Apa itu Eloquent & Kenapa Bukan Query SQL Manual
Eloquent ORM (Object-Relational Mapper) adalah penerjemah antara object PHP dan tabel relasional. Kita menyampaikan maksud dengan class serta method PHP, lalu Eloquent menyusun SQL yang sesuai, mengirimkannya ke database, dan mengubah hasilnya kembali menjadi object yang nyaman dipakai.
Eloquent mengikuti pola Active Record: satu object Model biasanya mewakili satu baris data. Misalnya object $siswa dapat mewakili baris siswa bernama Alya. Properti $siswa->nama mewakili nilai kolom nama, sedangkan method $siswa->save() menyimpan perubahan pada baris itu.
SQL tetap bekerja di balik layar—Eloquent bukan pengganti database. Bedanya, kita tidak perlu menyusun string SQL untuk operasi umum. Perbandingan berikut mempunyai maksud yang sama: mencari seluruh siswa dari kelas dengan ID 1.
SELECT * FROM siswas
WHERE kelas_id = 1;
use App\Models\Siswa;
$siswaKelasSatu = Siswa::where('kelas_id', 1)->get();
Versi Eloquent lebih dekat dengan bahasa pekerjaan: “dari Siswa, cari yang kelas_id-nya 1, lalu ambil.” Nama kolom dan nilai dikirim sebagai parameter, sehingga kita tidak menyambung input pengguna langsung ke string SQL. Ini membantu keterbacaan dan mengurangi risiko SQL injection ketika API Eloquent dipakai dengan benar.
Membuat Model (php artisan make:model, konvensi nama tabel otomatis)
Model adalah class PHP yang menjadi pintu masuk ke satu tabel. Buat Model dengan Artisan agar namespace dan lokasi file benar. Untuk data siswa, nama Model ditulis tunggal dengan huruf awal kapital: Siswa.
php artisan make:model Siswa
# File yang dihasilkan:
app/Models/Siswa.php
Eloquent memakai konvensi agar konfigurasi tidak perlu ditulis berulang. Model Siswa otomatis mencari tabel siswas: nama class diubah menjadi bentuk jamak dan snake_case. Contoh lain, Model DataSiswa akan mencari tabel data_siswas. Secara bawaan, Eloquent juga mengharapkan primary key id dan kolom waktu created_at serta updated_at.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Siswa extends Model
{
// Otomatis memakai tabel "siswas".
}
Jika tabel lama atau aturan sekolah memakai nama yang tidak sesuai konvensi—misalnya tabelnya memang bernama siswa—beri tahu Eloquent secara eksplisit melalui properti $table. Ini adalah pengecualian yang sah, tetapi untuk proyek baru sebaiknya ikuti konvensi supaya kode lebih ringkas.
class Siswa extends Model
{
protected $table = 'siswa';
}
siswa tetapi Model Siswa mencari siswas, akan muncul error “table not found”. Perbaiki dengan menyamakan nama tabel pada migration untuk proyek baru, atau tetapkan protected $table = 'siswa'; bila tabel yang ada tidak boleh diganti.$table untuk database lama, integrasi pihak lain, atau aturan penamaan yang memang tidak bisa diubah. Jangan menambahkan override jika hasil otomatis sudah benar.Mengambil Data — Read (all, find, where, first)
Operasi Read mengambil data tanpa mengubahnya. Pilih method berdasarkan pertanyaan yang sedang dijawab: semua siswa, satu siswa berdasarkan ID, beberapa siswa yang memenuhi syarat, atau record pertama saja.
all() langsung menjalankan query dan mengembalikan Collection berisi seluruh record. Ini nyaman untuk tabel latihan yang kecil, tetapi jangan digunakan sembarangan pada ribuan data karena semuanya dimuat ke memori sekaligus.
use App\Models\Siswa;
// Collection: bisa berisi 0, 1, atau banyak siswa
$semuaSiswa = Siswa::all();
// Satu Model dengan primary key 7, atau null jika tidak ada
$siswa = Siswa::find(7);
if ($siswa) {
echo $siswa->nama;
}
where() menambahkan syarat tetapi belum mengambil hasil; pada tahap ini nilainya masih query builder. Akhiri dengan get() jika membutuhkan banyak record, atau first() jika hanya membutuhkan satu record pertama. Kita dapat menambahkan beberapa where() sebelum query dijalankan.
// Banyak siswa dari kelas 1: hasilnya Collection
$siswaKelasSatu = Siswa::where('kelas_id', 1)
->get();
// Satu siswa pertama yang aktif: hasilnya Model atau null
$siswaAktif = Siswa::where('aktif', true)
->first();
// Record pertama dari seluruh tabel, atau null jika tabel kosong
$siswaPertama = Siswa::first();
Perhatikan bentuk hasilnya: all() dan get() menghasilkan Collection yang biasa diulang dengan foreach; find() dan first() menghasilkan satu Model atau null. Mengetahui bentuk hasil mencegah kita memperlakukan kumpulan data seperti satu siswa.
Siswa::where('kelas_id', 1) masih berupa builder, bukan daftar siswa. Tambahkan ->get() untuk banyak hasil. Sebaliknya, gunakan ->first() jika hanya butuh satu record agar maksud kode jelas dan database tidak mengirim seluruh hasil yang tidak dipakai.find($id) untuk satu primary key; where(...)->get() untuk daftar terfilter; where(...)->first() untuk satu hasil terfilter; all() hanya untuk kumpulan kecil. Untuk daftar besar gunakan pagination atau pemrosesan bertahap—akan dipelajari pada bagian lanjutan.Menyimpan & Mengubah Data — Create & Update (save, create, update, mass assignment $fillable)
Operasi Create menambahkan baris baru. Cara pertama adalah membuat object Model, mengisi setiap properti satu per satu, lalu memanggil save(). Cara ini eksplisit dan mudah dipahami saat field yang diisi sedikit.
use App\Models\Siswa;
$siswa = new Siswa();
$siswa->nama = 'Alya Putri';
$siswa->nis = '00125';
$siswa->kelas_id = 1;
$siswa->save();
Cara kedua adalah create([...]), yang mengisi banyak atribut sekaligus atau disebut mass assignment. Laravel sengaja memblokir pengisian massal untuk field yang belum diizinkan. Tanpa pembatasan, input dari form bisa saja menyisipkan field sensitif seperti is_admin, role, atau nilai lain yang seharusnya tidak boleh ditentukan pengguna.
Daftarkan hanya kolom yang memang boleh diisi bersama-sama di properti $fillable pada Model. $fillable bukan validasi: tipe, format, dan aturan bisnis input tetap harus divalidasi sebelum disimpan. Ia adalah pagar tambahan agar atribut yang tidak diharapkan tidak ikut masuk melalui create() atau update().
class Siswa extends Model
{
protected $fillable = [
'nama',
'nis',
'kelas_id',
];
}
// Create: ketiga key harus diizinkan dalam $fillable
$siswa = Siswa::create([
'nama' => 'Bima',
'nis' => '00126',
'kelas_id' => 1,
]);
// Update: ambil record, lalu ubah field yang diizinkan
$siswa = Siswa::findOrFail(7);
$siswa->update([
'nama' => 'Bima Pratama',
'kelas_id' => 2,
]);
update([...]) diterapkan pada Model yang sudah ditemukan dan langsung menyimpan perubahan. Alternatifnya, ubah properti seperti $siswa->nama lalu panggil save(). Untuk kode form, jangan langsung melakukan Siswa::create($request->all()); gunakan data hasil validasi dan biarkan $fillable menjadi lapisan perlindungan tambahan.
Siswa::create([...]) atau $siswa->update([...]) dapat memunculkan MassAssignmentException jika atribut belum diizinkan. Perbaiki dengan menambahkan hanya nama field yang aman ke $fillable—jangan membuka semua field hanya demi menghilangkan error.create() dan update() ketika memiliki array data tervalidasi serta daftar $fillable yang jelas. Pakai pengisian properti + save() ketika ingin mengatur atribut satu per satu atau menerapkan logika di antaranya. Jangan masukkan field sensitif ke $fillable bila pengguna tidak berhak mengubahnya.Menghapus Data — Delete (delete, kapan hati-hati)
Operasi Delete menghapus baris dari tabel. Jika object siswa sudah diambil, panggil delete() pada object tersebut. Ini enak dibaca: “siswa ini, hapus.”
$siswa = Siswa::find(7);
if ($siswa) {
$siswa->delete();
}
Jika ID sudah diketahui dan object-nya tidak diperlukan untuk pekerjaan lain, destroy($id) dapat dipakai sebagai jalan ringkas. Method ini mengembalikan jumlah record yang berhasil dihapus, sehingga hasilnya bisa diperiksa.
$jumlahTerhapus = Siswa::destroy(7);
if ($jumlahTerhapus === 0) {
echo 'Data siswa tidak ditemukan.';
}
Penghapusan biasa bersifat permanen pada database: Laravel tidak menampilkan kotak konfirmasi otomatis dan tidak menyediakan tombol undo. Konfirmasi di tampilan, validasi hak akses, serta aturan foreign key harus direncanakan oleh aplikasi. Data siswa juga mungkin dipakai oleh nilai, absensi, atau pembayaran, sehingga dampaknya lebih luas daripada satu baris.
Laravel memiliki konsep soft delete, yaitu menandai waktu penghapusan agar record bisa dipulihkan dan tidak muncul dalam query normal. Untuk hari ini cukup kenali konsepnya; implementasi trait, kolom deleted_at, dan pemulihan data akan dipelajari sebagai materi lanjutan.
$siswa->delete() dan Siswa::destroy($id) langsung menjalankan penghapusan. Jangan menguji pada database production. Pastikan ID benar, pengguna berwenang, ketergantungan data sudah diperiksa, dan backup tersedia untuk data penting.Mengelola Model & Query — 6 Kasus Nyata
CRUD dasar segera berkembang menjadi pertanyaan yang lebih spesifik: bagaimana menampilkan ribuan siswa tanpa membuat halaman berat, menggabungkan kondisi, atau menangani ID yang tidak ada? Eloquent menyediakan query builder yang dapat dirangkai. Query baru dijalankan ketika dipungkasi dengan method seperti get(), first(), atau paginate().
Kasus 1 — Ribuan data membuat halaman lambat
Masalah: halaman daftar memakai Siswa::all(). Ketika tabel berisi 20.000 siswa, seluruh baris diambil, dibuat menjadi object Model, dan ditahan di memori dalam satu request. Browser pun menerima halaman yang terlalu panjang.
Solusi: ambil data per halaman dengan paginate(). Database hanya mengirim jumlah yang dibutuhkan, misalnya 20 siswa, dan paginator membawa informasi halaman serta URL navigasi.
// Hindari Siswa::all() untuk tabel besar.
$siswa = Siswa::orderBy('nama')
->paginate(20);
return view('siswa.index', compact('siswa'));
@foreach ($siswa as $item)
<p>{{ $item->nis }} — {{ $item->nama }}</p>
@endforeach
{{ $siswa->links() }}
Parameter ?page=2 dibaca otomatis. Mulailah dari 15–25 record per halaman. Jika hanya butuh tombol sebelumnya/berikutnya, simplePaginate() dapat menghindari query hitung total.
Siswa::get()->paginate(20) salah karena get() sudah mengambil semua data sebagai Collection. Panggil paginate() langsung pada builder.Kasus 2 — Kondisi pencarian kompleks
Masalah: sekolah ingin siswa dari kelas tertentu dan aktif. Di layar lain, operator butuh siswa aktif atau orientasi, terbatas pada beberapa kelas.
Solusi: dua where() berurutan berarti AND. Gunakan orWhere() untuk alternatif dan whereIn() untuk daftar nilai.
// kelas 3 DAN aktif
$siswaAktif = Siswa::where('kelas_id', 3)
->where('aktif', true)
->get();
// Aktif DAN kelas termasuk 1, 2, 3
$siswaPilihan = Siswa::where('aktif', true)
->whereIn('kelas_id', [1, 2, 3])
->get();
Untuk AND/OR campuran, kelompokkan alternatif dalam closure. Tanpa grouping, orWhere() dapat memperluas hasil di luar kelas yang dimaksud.
$siswa = Siswa::where('kelas_id', 3)
->where(function ($query) {
$query->where('aktif', true)
->orWhere('status', 'orientasi');
})
->get();
Kasus 3 — Hanya membutuhkan nama dan NIS
Masalah: dropdown hanya menampilkan nama dan nis, tetapi query mengambil semua kolom, termasuk data yang tidak dipakai.
Solusi: pakai select() agar database hanya mengirim kolom yang diperlukan. Ini mengurangi transfer data dan penggunaan memori.
$ringkasan = Siswa::select('id', 'nama', 'nis')
->where('aktif', true)
->orderBy('nama')
->get();
Kolom yang tidak dipilih tidak tersedia pada object hasil. Sertakan id jika hasil dipakai untuk tautan detail, update, atau relasi. select() bukan mekanisme otorisasi.
Kasus 4 — Data tidak ditemukan atau null
Masalah: URL meminta /siswa/9999, tetapi ID tidak ada. find() menghasilkan null; akses langsung ke $siswa->nama akan gagal.
Solusi: find($id) cocok ketika kondisi tidak ditemukan ingin ditangani sendiri. findOrFail($id) melempar ModelNotFoundException, yang pada request web otomatis menjadi respons 404.
$siswa = Siswa::find($id);
if ($siswa === null) {
return redirect()->route('siswa.index')
->with('error', 'Siswa tidak ditemukan.');
}
public function show(int $id)
{
$siswa = Siswa::findOrFail($id);
return view('siswa.show', compact('siswa'));
}
Gunakan findOrFail() untuk halaman detail, edit, update, dan delete ketika URL wajib menunjuk record valid. Pakai find() saat alur bisnis membutuhkan fallback atau pesan khusus.
$siswa?->nama mencegah error, tetapi dapat menyamarkan data wajib yang hilang. Tetap tentukan apakah responsnya redirect, pesan, atau 404.Kasus 5 — Hasil harus berurutan
Masalah: database tidak menjamin urutan tanpa instruksi. Daftar yang sama dapat muncul dalam urutan berbeda.
Solusi: pakai orderBy(). latest() mengurutkan created_at terbaru dahulu, sedangkan oldest() menampilkan terlama dahulu.
$namaAZ = Siswa::orderBy('nama', 'asc')->get();
$terbaru = Siswa::latest()->paginate(20);
$terlama = Siswa::oldest()->get();
$perKelas = Siswa::orderBy('kelas_id')
->orderBy('nama')
->get();
Jika tabel tidak punya timestamps, berikan kolom lain seperti latest('tanggal_masuk'), atau gunakan orderBy() eksplisit.
Kasus 6 — Kapan Eloquent terlalu berat?
Masalah: laporan membutuhkan banyak agregasi, grouping, join, dan puluhan ribu baris. Eloquent mengubah setiap hasil menjadi object Model (hydration), padahal laporan hanya butuh angka ringkas.
Solusi: ukur dahulu. Bila bottleneck terbukti pada query atau hydration, pertimbangkan Query Builder lewat DB::table(). Hasilnya object sederhana tanpa event, accessor, relasi, dan method Active Record.
use Illuminate\Support\Facades\DB;
$rekap = DB::table('siswas')
->select('kelas_id', DB::raw('COUNT(*) as jumlah'))
->where('aktif', true)
->groupBy('kelas_id')
->get();
Pertimbangkan Query Builder atau raw SQL untuk agregasi berat, laporan lintas banyak tabel, bulk operation, atau query khusus yang terbukti lambat. Raw SQL wajib memakai parameter binding; jangan menyisipkan input pengguna ke string.
Praktik Mini — CRUD Data Siswa
Gunakan Model Siswa dari Day 4 dan jalankan melalui Laravel Tinker agar fokus pada Eloquent.
1. Siapkan Model dan $fillable
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Siswa extends Model
{
protected $fillable = ['nama', 'nis', 'kelas_id'];
}
Pastikan migration sudah berjalan dan kelas dengan ID 1 serta 2 tersedia karena kelas_id adalah foreign key.
2. Tampilkan semua siswa
php artisan migrate:status
php artisan tinker
use App\Models\Siswa;
$semua = Siswa::orderBy('nama')->get();
$semua->each(fn ($s) => dump([$s->id, $s->nis, $s->nama]));
3. Cari satu siswa berdasarkan ID
$id = 1;
$siswa = Siswa::find($id);
$siswa ? $siswa->nama : "Siswa ID {$id} tidak ditemukan";
4. Tambahkan siswa dengan create()
$siswaBaru = Siswa::create([
'nama' => 'Nadia Rahma',
'nis' => '2026001',
'kelas_id' => 1,
]);
$siswaBaru->id;
5. Update kelas siswa
$siswaBaru = Siswa::findOrFail($siswaBaru->id);
$siswaBaru->update(['kelas_id' => 2]);
$siswaBaru->refresh()->kelas_id;
6. Hapus data latihan
$idLatihan = $siswaBaru->id;
$siswaBaru->delete();
Siswa::find($idLatihan); // null = berhasil dihapus
Tugas / PR — Uji Pemahaman Hari Ini
Sekolah meminta fitur kelola data guru. Buat migration dan Model Guru dengan kolom nama, nip, mapel, dan aktif. Buat nip unik, lalu kerjakan tanpa menyalin solusi lengkap siswa.
Soal 1 — Dasar: tampilkan dan cari guru
- Tampilkan seluruh guru urut nama A–Z.
- Cari satu guru berdasarkan ID dan tangani ID yang tidak ada.
Kriteria selesai: daftar dapat diulang, urutannya benar, dan ID tidak tersedia tidak menyebabkan error pada null.
orderBy(...)->get(), lalu pilih find() atau findOrFail().Soal 2 — Menengah: create dan update
- Atur
$fillablehanya untuk empat field yang boleh diisi. - Tambah satu guru dengan
create(). - Ubah
mapeldanaktifdenganupdate().
Kriteria selesai: tidak ada MassAssignmentException, record baru muncul, perubahan bertahan setelah diambil ulang, dan field tak diperlukan tidak masuk $fillable.
$fillable.Soal 3 — Tantangan terbuka
Tampilkan hanya guru aktif, urut nama A–Z, sebanyak 10 guru per halaman.
Kriteria selesai: guru nonaktif tidak ikut, urutan benar, tiap halaman maksimal 10 record, dan links() dapat dirender di Blade.
where(), orderBy(), lalu paginate().Format PR
- Commit migration, Model, dan controller/command pembuktian.
- Sertakan README berisi cara migrate dan tiga skenario uji.
- Jangan commit
.env, password, atau data pribadi sungguhan. - Judul:
Day 5: CRUD dan query data guru.
Playground PHP — Simulasi Eloquent Active Record
Eloquent asli membutuhkan Laravel dan database. Playground ini menjalankan PHP murni di browser melalui WebAssembly (php-wasm). Class sederhana berikut menyimpan array dan meniru pola all/find/create/update/delete. Data direset setiap kode dijalankan.
Mesin PHP sedang disiapkan…
Latihan Mandiri & Checklist Selesai
Latihan mandiri
- Ubah playground agar
find()mencari ID yang tidak ada dan tampilkan pesan aman. - Tambah field
aktifke data simulasi serta$fillable, lalu buat method siswa aktif. - Di Laravel, tampilkan siswa aktif urut nama dengan kolom
id,nama, dannissaja. - Bandingkan
find()danfindOrFail()pada ID yang tidak tersedia. - Ubah daftar siswa dari
all()menjadipaginate(20)dan renderlinks().
Checklist sebelum lanjut Day 6
- Bisa menjelaskan hubungan Model, object record, dan tabel.
- Bisa menjalankan CRUD Eloquent tanpa SQL manual.
- Bisa mengatur
$fillabledan tahu bahwa ia bukan pengganti validasi. - Bisa menangani
nullserta memilihfind()ataufindOrFail(). - Bisa merangkai filter,
select(), urutan, dan pagination. - Tahu kapan mempertimbangkan Query Builder untuk laporan kompleks yang terbukti lambat.
Day 6 akan memakai Model ini untuk Relationships dan Form Handling.
🏁 Milestone Day 5 Tercapai!
Kamu sudah mampu melakukan CRUD dengan Eloquent tanpa SQL manual, memahami perlindungan mass assignment melalui $fillable, dan menyusun query yang aman serta efisien. Fondasi Model sudah kuat—besok kamu siap lanjut ke Relationships dan Form Handling.