Day 2 — Routing & Blade Views Dasar
Hari ini Bu Andri akan menghubungkan alamat web dengan tampilan yang rapi: menerima parameter, menyiapkan data sederhana, lalu menampilkannya memakai Blade.
Target Belajar Hari Ini
Selamat datang kembali, Bu Andri! Kemarin kita membuat route pertama. Hari ini route itu tidak lagi mengirim HTML mentah. Kita akan memisahkan alamat, data, dan tampilan agar aplikasi lebih mudah dibaca dan dirawat.
- Menjelaskan fungsi route dengan kata-kata sendiri.
- Membuat route wajib dan opsional yang menerima parameter URL.
- Mengirim data dari route ke file view.
- Memakai
{{ }},@if, dan@foreachdi Blade. - Membuat layout dasar dengan
@extendsdan@section. - Menampilkan daftar siswa dari array PHP tanpa database.
1. Review Singkat Routing
Route adalah daftar alamat yang dikenal aplikasi. Bayangkan meja informasi sekolah: ketika pengunjung menyebut tujuan, petugas mengarahkannya ke layanan yang tepat. Route web Laravel ditulis di routes/web.php.
use Illuminate\Support\Facades\Route;
Route::get('/halo', function () {
return 'Halo, Bu Andri!';
});
| Bagian | Artinya |
|---|---|
Route::get | Menerima permintaan GET, biasanya saat membuka halaman. |
'/halo' | Alamat setelah nama domain, misalnya localhost:8000/halo. |
function () | Pekerjaan yang dijalankan ketika alamat tersebut dibuka. |
return | Jawaban yang dikirim kembali ke browser. |
/siswaRoute::get() saat menampilkan halaman. Nanti, saat membuat form, kita akan mengenal POST untuk mengirim atau menyimpan data.Kenapa route dibutuhkan?
Tanpa route, server tidak tahu file mana yang harus dijalankan saat browser meminta alamat tertentu. Laravel modern (berbeda dari PHP polos yang satu file = satu alamat) memusatkan semua alamat di satu tempat, routes/web.php, sehingga mudah dicari dan dirawat — mirip daftar isi buku yang menunjukkan halaman mana berisi bab apa, tanpa harus membongkar seluruh buku.
Kesalahan umum pemula
Route::get('halo', function () {
return 'Halo!';
});
// Dibuka lewat: http://127.0.0.1:8000/Halo (huruf besar)
Path route bersifat case-sensitive. /halo dan /Halo dianggap alamat berbeda, sehingga muncul 404 meski route sudah ditulis benar. Perbaikannya: samakan huruf pada URL yang diketik dengan huruf pada definisi route, dan biasakan menulis path route memakai huruf kecil semua.
routes/web.php tetap ringkas dan mudah dibaca.2. Route dengan Parameter
Parameter adalah bagian alamat yang nilainya dapat berubah. Pada /siswa/{id}, teks di dalam kurung kurawal menjadi “kotak” untuk menerima nilai dari URL.
Route::get('/siswa/{id}', function (string $id) {
return "Membuka siswa dengan ID: " . $id;
});
Coba buka http://127.0.0.1:8000/siswa/7. Laravel mengisi $id dengan teks 7. Nama {id} dan $id harus sama agar mudah dipahami.
Lebih dari satu parameter
Route::get('/kelas/{kelas}/siswa/{nama}', function (
string $kelas,
string $nama
) {
return "Siswa $nama berada di kelas $kelas.";
});
Alamat /kelas/7A/siswa/Alya akan menghasilkan kalimat “Siswa Alya berada di kelas 7A.” Urutan argumen mengikuti urutan parameter pada URL.
Parameter opsional dan nilai bawaan
Route::get('/salam/{nama?}', function (string $nama = 'Tamu') {
return "Selamat datang, $nama!";
});
Tanda tanya pada {nama?} berarti parameter boleh tidak diisi. Membuka /salam memakai nilai bawaan “Tamu”, sedangkan /salam/Andri memakai “Andri”.
Membatasi bentuk parameter
Route::get('/siswa/{id}', function (string $id) {
return "ID siswa: $id";
})->whereNumber('id');
whereNumber('id') memastikan bagian id hanya angka. Jika membuka /siswa/abc, Laravel memberikan halaman 404 karena alamat tidak sesuai aturan.
{{ }}, karakter HTML berbahaya diamankan secara otomatis.❓ Apa beda /siswa dan /siswa/{id}?
/siswa biasanya menampilkan daftar semua siswa. /siswa/{id} menampilkan satu siswa tertentu berdasarkan nilai ID pada URL.
Kenapa parameter dibutuhkan?
Tanpa parameter, tiap siswa butuh route sendiri (/siswa-1, /siswa-2, dst) — tidak masuk akal untuk ratusan siswa. Parameter membuat satu route /siswa/{id} bisa melayani semua siswa, mirip nomor kamar hotel: satu format alamat, isi kamarnya beda-beda tergantung nomor yang diketik.
Kesalahan umum pemula
Route::get('/siswa/{id}', function (string $id) {
return "Detail siswa: $id";
})->whereNumber('id');
Route::get('/siswa/aktif', function () {
return "Daftar siswa aktif";
});
// Membuka /siswa/aktif akan gagal karena "aktif" tertangkap {id} lebih dulu,
// lalu ditolak whereNumber('id')
Laravel mencocokkan route dari atas ke bawah. Jika route dengan parameter fleksibel diletakkan sebelum route dengan path statis yang mirip, path statis bisa ikut tertangkap atau malah tertolak. Perbaikannya: letakkan route path statis (/siswa/aktif) di atas route berparameter (/siswa/{id}) supaya dicocokkan lebih dulu.
{id}) kalau halaman tidak bermakna tanpa nilai itu, misalnya detail satu siswa. Pakai parameter opsional ({nama?}) hanya untuk nilai yang wajar punya default, misalnya halaman sapaan umum. Jangan buat ID siswa opsional — "siswa tanpa ID" tidak jelas maksudnya dan berpotensi bug.3. Mengirim Data dari Route ke View
HTML panjang sebaiknya tidak ditulis di dalam route. Laravel menyediakan view: file tampilan berakhiran .blade.php yang disimpan di resources/views.
Menerima alamat dan menentukan tampilan yang harus dibuka.
Mengatur bagaimana data disajikan menjadi halaman HTML.
Langkah 1 — buat file view
Buat file resources/views/siswa/detail.blade.php. Folder siswa membantu mengelompokkan tampilan yang berhubungan dengan siswa.
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>Detail Siswa</title>
</head>
<body>
<h1>Detail Siswa</h1>
<p>ID: {{ $id }}</p>
<p>Nama: {{ $nama }}</p>
</body>
</html>
Langkah 2 — kirim data dari route
Route::get('/siswa/{id}', function (string $id) {
$nama = 'Alya Putri';
return view('siswa.detail', [
'id' => $id,
'nama' => $nama,
]);
})->whereNumber('id');
view('siswa.detail') menunjuk ke resources/views/siswa/detail.blade.php. Tanda titik menggantikan garis miring folder. Array kedua mengirim data: key 'nama' menjadi variabel $nama di view.
Alternatif ringkas dengan compact()
Route::get('/siswa/{id}', function (string $id) {
$nama = 'Alya Putri';
return view('siswa.detail', compact('id', 'nama'));
});
compact('id', 'nama') membuat array dari variabel $id dan $nama. Untuk pemula, bentuk array lengkap lebih eksplisit; pakai compact() setelah paham pasangan key dan value.
Kenapa data harus dikirim lewat view, bukan ditulis di route?
Menulis HTML panjang langsung di route mencampur dua tanggung jawab berbeda: "apa yang terjadi" (logika) dan "bagaimana tampilannya" (presentasi). Kalau desainer mengubah tampilan, ia harus mengubah file PHP yang sama dengan tempat logika ditulis — risiko tabrakan dan salah edit meningkat. Memisahkan ke view membuat setiap file punya satu tanggung jawab jelas, seperti resep masakan (logika) dipisah dari cara menata piring (tampilan).
Kesalahan umum pemula
return view('siswa.detail', [
'nama_siswa' => $nama,
]);
// di detail.blade.php:
<p>Nama: {{ $nama }}</p>
// Error: Undefined variable $nama
Key array ('nama_siswa') menjadi nama variabel di view — harus dieja persis sama. Salah ketik atau beda nama antara route dan view adalah penyebab error "Undefined variable" paling umum bagi pemula. Perbaikannya: samakan nama key dengan nama variabel yang dipakai di file Blade, atau pakai compact() agar nama otomatis mengikuti nama variabel PHP.
compact()?Array manual (['id' => $id]) lebih jelas dibaca saat baru belajar atau saat nama variabel dan key sengaja dibuat beda. compact('id', 'nama') lebih ringkas dan cocok dipakai setelah terbiasa, terutama saat variabel yang dikirim banyak dan namanya sudah konsisten.4. Dasar Blade Templating
Blade adalah mesin template bawaan Laravel. Blade membolehkan kita menulis HTML biasa sekaligus menyisipkan data dan aturan PHP dengan sintaks yang lebih enak dibaca.
Menampilkan data dengan {{ }}
<h1>Halo, {{ $nama }}!</h1>
<p>Kelas: {{ $kelas }}</p>
Kurung kurawal ganda menampilkan nilai dan otomatis melakukan escaping. Artinya, jika isi data mengandung tag HTML, Blade menampilkannya sebagai teks, bukan menjalankannya.
{!! !!} untuk data penggunaSintaks tersebut menampilkan HTML mentah tanpa pengamanan. Untuk nama siswa, pesan, atau isi form, selalu gunakan {{ }}.Percabangan dengan @if
@if ($aktif)
<span>Aktif</span>
@else
<span>Tidak aktif</span>
@endif
@if memilih tampilan berdasarkan kondisi. Setiap pembuka harus ditutup dengan @endif. Untuk kondisi tambahan tersedia @elseif.
Perulangan dengan @foreach
<ul>
@foreach ($mapel as $namaMapel)
<li>{{ $namaMapel }}</li>
@endforeach
</ul>
@foreach mengulang bagian HTML untuk setiap isi array. Setiap pembuka @foreach harus ditutup dengan @endforeach.
Saat daftar mungkin kosong: @forelse
<ul>
@forelse ($siswa as $item)
<li>{{ $item['nama'] }}</li>
@empty
<li>Belum ada data siswa.</li>
@endforelse
</ul>
@forelse praktis karena sudah menyediakan tampilan kosong. Pengguna tidak akan melihat area putih membingungkan ketika array belum berisi data.
| Blade | Kegunaan | Penutup |
|---|---|---|
{{ $data }} | Menampilkan nilai dengan aman | Tidak ada |
@if (...) | Memilih tampilan berdasarkan kondisi | @endif |
@foreach (...) | Mengulang tampilan untuk setiap data | @endforeach |
@forelse (...) | Mengulang sekaligus menangani data kosong | @endforelse |
Kenapa Blade, bukan PHP mentah di HTML?
PHP polos bisa saja dicampur langsung ke HTML dengan <?php ?>, tapi hasilnya cepat berantakan dan gampang lupa menutup tag. Blade menyediakan sintaks pendek (@if, @foreach) yang lebih mudah dipindai mata, plus otomatis meng-escape output — perlindungan yang gampang lupa ditulis kalau pakai PHP manual (htmlspecialchars() di setiap echo).
Kesalahan umum pemula
@if ($aktif)
<span>Aktif</span>
// lupa @endif — halaman error: "syntax error, unexpected end of file"
Setiap direktif pembuka (@if, @foreach, @section) wajib punya penutup pasangannya. File Blade yang lupa ditutup akan gagal di-compile dan menampilkan error PHP yang membingungkan. Perbaikannya: selalu tulis pasangan buka-tutup sekaligus sebelum mengisi isinya, atau andalkan auto-indent editor untuk mendeteksi blok yang belum menutup.
@foreach vs @forelse?Pakai @forelse setiap kali data mungkin kosong, misalnya hasil pencarian atau daftar yang bisa difilter — supaya pengguna melihat pesan jelas, bukan tabel kosong membingungkan. Pakai @foreach biasa hanya kalau kamu yakin data pasti selalu ada isinya (jarang terjadi di aplikasi nyata).5. Layout Dasar: Tampilan yang Bisa Dipakai Ulang
Jika lima halaman menulis menu, judul situs, dan footer masing-masing, perubahan kecil harus dilakukan lima kali. Layout menyimpan kerangka umum satu kali, lalu setiap halaman hanya mengisi bagian yang berbeda.
Langkah 1 — buat layout utama
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>@yield('title', 'Sekolah App')</title>
</head>
<body>
<nav>
<a href="/">Beranda</a>
<a href="/siswa">Daftar Siswa</a>
</nav>
<main>
@yield('content')
</main>
<footer>Sekolah App · {{ date('Y') }}</footer>
</body>
</html>
@yield('content') adalah tempat kosong yang akan diisi halaman anak. Nilai kedua pada @yield('title', 'Sekolah App') menjadi judul bawaan.
Langkah 2 — gunakan layout dari halaman
@extends('layouts.app')
@section('title', 'Daftar Siswa')
@section('content')
<h1>Daftar Siswa</h1>
<p>Isi halaman khusus daftar siswa berada di sini.</p>
@endsection
@extends memilih layout induk. @section mengisi slot bernama sama dengan @yield. Untuk bagian satu baris, seperti judul, boleh memakai bentuk ringkas.
Alternatif kecil dengan @include
@include cocok untuk potongan kecil yang dipakai ulang, misalnya pesan informasi atau menu.
<div class="info">
{{ $pesan }}
</div>
@include('partials.info', [
'pesan' => 'Data siswa diperbarui setiap hari.'
])
Seperti gedung sekolah: bentuk umum, lorong, dan pintunya tetap.
Seperti isi tiap ruangan: berbeda sesuai fungsi, tetapi berada di gedung yang sama.
Kenapa layout dibutuhkan?
Tanpa layout, setiap halaman baru berarti copy-paste seluruh kerangka HTML (navbar, footer, meta tag). Kalau suatu hari navbar perlu ditambah menu, kamu harus mengedit ulang di setiap file — rawan ada yang lupa dan jadi tidak konsisten. Layout menyimpan kerangka itu satu kali di satu file, mirip cetakan kue: bentuk luar sama, isian di dalamnya beda-beda.
Kesalahan umum pemula
<!-- layouts/app.blade.php -->
@yield('content')
<!-- siswa/index.blade.php -->
@extends('layouts.app')
@section('isi')
<h1>Daftar Siswa</h1>
@endsection
// Halaman tampil kosong, tidak ada error
Nama pada @section('isi') harus persis sama dengan nama pada @yield('content') di layout. Kalau beda, Blade tidak menampilkan error — halaman cuma tampil kosong tanpa isi, yang justru lebih membingungkan untuk pemula. Perbaikannya: samakan nama slot antara @section dan @yield, dan cek dulu nama yang dipakai layout sebelum menulis halaman anak.
@extends vs @include?@extends dipakai sekali per halaman untuk kerangka besar (satu halaman = satu layout). @include dipakai berkali-kali untuk potongan kecil yang berulang di banyak tempat, seperti kartu info atau tombol. Jangan pakai @include untuk kerangka penuh halaman — itu tugas @extends.6. Praktik Mini: Halaman Daftar Siswa
Kita akan membuat satu halaman lengkap yang menampilkan array siswa. Ikuti berurutan dan ketik ulang agar tangan terbiasa.
Langkah 1 — siapkan route dan data
use Illuminate\Support\Facades\Route;
Route::get('/siswa', function () {
$siswa = [
['id' => 1, 'nama' => 'Alya Putri', 'kelas' => '7A', 'aktif' => true],
['id' => 2, 'nama' => 'Bima Akbar', 'kelas' => '7B', 'aktif' => false],
['id' => 3, 'nama' => 'Citra Lestari', 'kelas' => '7A', 'aktif' => true],
];
return view('siswa.index', [
'judul' => 'Daftar Siswa',
'siswa' => $siswa,
]);
});
Langkah 2 — buat layout sederhana
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>@yield('title', 'Sekolah App')</title>
<style>
body { font-family: Arial, sans-serif; max-width: 800px; margin: 40px auto; }
table { width: 100%; border-collapse: collapse; }
th, td { border: 1px solid #ddd; padding: 10px; text-align: left; }
th { background: #f1f5f9; }
.aktif { color: green; font-weight: bold; }
.nonaktif { color: #b91c1c; }
</style>
</head>
<body>
<nav><a href="/siswa">Daftar Siswa</a></nav>
<hr>
@yield('content')
</body>
</html>
Langkah 3 — tampilkan array dengan Blade
@extends('layouts.app')
@section('title', $judul)
@section('content')
<h1>{{ $judul }}</h1>
<p>Total data: {{ count($siswa) }} siswa</p>
<table>
<thead>
<tr>
<th>No.</th>
<th>Nama</th>
<th>Kelas</th>
<th>Status</th>
</tr>
</thead>
<tbody>
@forelse ($siswa as $item)
<tr>
<td>{{ $loop->iteration }}</td>
<td>
<a href="/siswa/{{ $item['id'] }}">
{{ $item['nama'] }}
</a>
</td>
<td>{{ $item['kelas'] }}</td>
<td>
@if ($item['aktif'])
<span class="aktif">Aktif</span>
@else
<span class="nonaktif">Tidak aktif</span>
@endif
</td>
</tr>
@empty
<tr>
<td colspan="4">Belum ada data siswa.</td>
</tr>
@endforelse
</tbody>
</table>
@endsection
Variabel khusus $loop->iteration otomatis tersedia di dalam loop Blade dan dimulai dari angka 1. Setelah menyimpan file, buka http://127.0.0.1:8000/siswa.
Langkah 4 — tambahkan route detail sederhana
Route::get('/siswa/{id}', function (string $id) {
return view('siswa.detail', [
'id' => $id,
'nama' => 'Contoh Siswa',
]);
})->whereNumber('id');
❓ Mengapa data contoh ditulis di route?
Agar latihan hari ini fokus pada routing dan Blade. Ini bukan pola akhir aplikasi. Setelah belajar database dan Eloquent, array tersebut akan diganti data dari database dan pengambilannya diletakkan di Controller.
Kenapa latihan ini digabung jadi satu praktik utuh?
Belajar route, data, dan Blade satu-satu itu penting, tapi baru terasa nyambung kalau dipraktikkan sebagai satu alur utuh: alamat dibuka → data disiapkan → Blade merender tabel. Praktik mini ini sengaja meniru pola paling umum di aplikasi nyata: halaman daftar (index) dan halaman detail (show) — pola yang akan terus dipakai bahkan setelah data berpindah dari array ke database.
Kesalahan umum pemula
return view('siswa.index', [...]);
// tapi file disimpan di:
// resources/views/siswa/Index.blade.php (huruf besar)
// atau: resources/views/siswa-index.blade.php (pakai strip, bukan folder)
Penulisan view('siswa.index') mencari file resources/views/siswa/index.blade.php persis — nama folder dan file harus sama persis (huruf kecil, tanda titik menggantikan garis miring). Kesalahan ejaan folder/file adalah penyebab error "View not found" paling umum. Perbaikannya: cocokkan nama file dan folder betul-betul sama dengan yang ditulis di view(...), dan konsisten pakai huruf kecil untuk nama file Blade.
Tugas / PR — Uji Pemahaman Hari Ini
Studi kasus: Bu Andri juga perlu halaman daftar guru, mirip dengan halaman daftar siswa yang baru dibuat. Kerjakan tiga soal berikut secara berurutan di proyek Laravel lokal kamu, tanpa database — cukup array PHP seperti praktik mini di atas.
id, nama, mapel, dan aktif (boolean), mirip struktur array $siswa pada Praktik Mini.Soal A (Dasar) — Halaman Daftar Guru
Buat route /guru yang mengirim array minimal 4 guru ke view guru/index.blade.php. Tampilkan sebagai tabel memakai @forelse, dengan kolom No., Nama, dan Mapel. Gunakan layout yang sudah ada (layouts/app.blade.php) lewat @extends.
Hint: pola sama persis dengan langkah 1–3 Praktik Mini, cuma ganti "siswa" jadi "guru".
- Route
/guruterbuka tanpa error. - Tabel menampilkan minimal 4 baris data dari array, bukan ditulis manual satu-satu.
- Halaman memakai layout yang sama dengan
/siswa(tidak menyalin ulang kerangka HTML).
Soal B (Menengah) — Detail Guru dengan Status
Buat route /guru/{id} yang menerima parameter ID (batasi hanya angka), lalu tampilkan nama dan mapel guru tersebut di view guru/detail.blade.php. Tambahkan kondisi Blade: jika aktif bernilai true tampilkan "Sedang Mengajar", jika tidak tampilkan "Cuti". Dari halaman daftar (Soal A), tiap baris nama guru harus jadi tautan menuju halaman detailnya.
Hint: gabungkan pola route parameter + whereNumber + @if/@else dari materi bagian 2 dan 4.
- Route
/guru/{id}menolak input non-angka (menampilkan 404). - Status "Sedang Mengajar"/"Cuti" tampil sesuai data, memakai
@if, bukan ditulis manual. - Tautan dari tabel daftar guru berhasil membuka halaman detail guru yang benar.
Soal C (Tantangan) — Rancang Sendiri
Sekolah ingin halaman /guru/mapel/{nama_mapel} yang menampilkan daftar guru pengampu satu mata pelajaran tertentu saja (misalnya /guru/mapel/Matematika menampilkan hanya guru Matematika). Rancang sendiri route, cara menyaring data guru berdasarkan mapel, dan tampilan Blade-nya — termasuk apa yang terjadi kalau mapel yang dicari tidak ada gurunya sama sekali.
Hint: pikirkan urutan route ini dengan /guru/{id} — mana yang harus didaftarkan lebih dulu?
- Bisa menjelaskan dengan kata sendiri kenapa urutan route yang dipilih tidak menimbulkan konflik dengan
/guru/{id}. - Halaman tetap menampilkan pesan yang jelas (bukan error atau halaman kosong) saat mapel tidak ditemukan.
- Kode penyaringan data (filter array) berjalan tanpa mengubah struktur array guru asli.
Playground PHP — Simulasi Data untuk Blade
Blade berjalan di dalam Laravel, sedangkan playground ini menjalankan PHP sungguhan di browser memakai WebAssembly (php-wasm). Kita memakai PHP biasa untuk melatih array, kondisi, perulangan, dan pengamanan output—logika yang sama di balik template Blade.
Mesin PHP sedang disiapkan…
foreach (...) { } di PHP menjadi @foreach (...) ... @endforeach di Blade. Ternary untuk status dapat diganti @if ... @else ... @endif.Latihan Mandiri & Checklist Selesai
Latihan A — di playground
- Pilih “Daftar siswa”, lalu tambahkan siswa bernama Dinda dari kelas 8A.
- Ubah status Bima menjadi aktif dan perhatikan hasilnya.
- Pilih “Daftar kosong” lalu tambahkan satu data agar pesan kosong tidak muncul.
- Pilih “Output aman” dan perhatikan perbedaan teks mentah dengan
htmlspecialchars().
Latihan B — di proyek Laravel
- Buat route
/guru/{nama}dan kirim parameter nama ke view. - Buat halaman
guru/index.blade.phpyang menampilkan minimal tiga guru dengan@foreach. - Tambahkan key
aktif, lalu tampilkan “Aktif” atau “Cuti” memakai@if. - Buat
layouts/app.blade.php, lalu gunakan dengan@extendsdan@section. - Kosongkan array sementara dan berikan pesan ramah memakai
@forelse.
Checklist sebelum lanjut
- Route
/siswaberhasil dibuka tanpa error. - Route
/siswa/{id}menerima angka dari URL. - Data array dari route tampil di file Blade.
- Daftar dibuat dengan loop, bukan menulis baris satu per satu.
- Status berbeda tampil memakai kondisi Blade.
- Halaman menggunakan layout yang bisa dipakai ulang.
- Bisa menjelaskan perbedaan route, data, dan view.
🎉 Day 2 Selesai jika…
Halaman /siswa menampilkan minimal tiga data dari array melalui Blade, route detail menerima parameter ID, status tampil dengan kondisi, dan tampilan memakai layout dasar tanpa menyalin kerangka HTML berulang kali.