Laravel · Bab 6
Lab 3: Blade Layout, Routing & READ
Mendaftarkan resource route, membuat master layout Blade dengan Bootstrap, lalu menampilkan katalog produk beserta kategorinya lewat controller dan eager loading.
Di lab ini Smart Retail App mulai punya tampilan: halaman katalog produk yang menampilkan kode, nama, kategori, harga, dan stok. Kamu menyusunnya dengan urutan MVC — route, layout, controller, lalu view. Contoh kode memakai studi kasus perpustakaan (books dan authors); terapkan polanya ke produk dan kategori.
Alur Langkah
- Buka
routes/web.php, ganti seluruh isinya: arahkan/ke halaman indeks produk dan daftarkan resource route untuk produk (besertausecontrollernya). - Buat folder
resources/views/layouts/, lalu buatlayouts/app.blade.phpberisi kerangka HTML, Bootstrap 5.3 dan Bootstrap Icons, navbar (tautan katalog, tautan transaksi, status login), kotak pesansuccess/error, dan@yield('content'). - Jalankan
php artisan make:controller <NamaController>untuk controller produk, lalu isi methodindex()yang mengambil produk beserta kategorinya (eager loading, urut terbaru) dan mengirimkannya ke view. - Buat folder
resources/views/<folder>/untuk produk, lalu buatindex.blade.phpberisi tabel katalog: nomor, kode, nama, kategori, harga berformat rupiah, badge stok, serta tombol Edit dan Delete. - Buka
http://127.0.0.1:8000dan pastikan kamu diarahkan ke halaman katalog.
Route Resource
Satu baris Route::resource mendaftarkan tujuh rute CRUD sekaligus:
use App\Http\Controllers\BookController;
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return redirect()->route('books.index');
});
Route::resource('books', BookController::class);
| Method HTTP | URI | Nama rute | Method controller |
|---|---|---|---|
| GET | /books | books.index | index — daftar data |
| GET | /books/create | books.create | create — form tambah |
| POST | /books | books.store | store — simpan data baru |
| GET | /books/{book} | books.show | show — detail |
| GET | /books/{book}/edit | books.edit | edit — form ubah |
| PUT/PATCH | /books/{book} | books.update | update — simpan perubahan |
| DELETE | /books/{book} | books.destroy | destroy — hapus |
Nama rute dipakai di view lewat helper route(), jadi alamat URL cukup diubah di satu tempat.
Master Layout Blade
Layout adalah kerangka yang dipakai semua halaman. Bagian yang berbeda di tiap halaman ditandai @yield:
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>@yield('title', 'Perpustakaan')</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<main class="container my-4">
@if(session('success'))
<div class="alert alert-success">{{ session('success') }}</div>
@endif
@yield('content')
</main>
</body>
</html>
Halaman anak mewarisi layout dengan @extends, lalu mengisi setiap bagian dengan @section:
@extends('layouts.app')
@section('title', 'Katalog Buku')
@section('content')
<h5>Katalog Buku</h5>
...
@endsection
Navbar juga memuat tautan ke halaman yang rutenya baru dibuat di lab berikutnya (transaksi di Lab 5, login/logout di Lab 7). Memanggil route() untuk nama rute yang belum terdaftar langsung memicu galat Route [...] not defined, jadi bungkus tautan itu dengan pemeriksaan Route::has(...):
<a class="nav-link" href="{{ Route::has('loans.index') ? route('loans.index') : '#' }}">Peminjaman</a>
@if(Route::has('login'))
<a href="{{ route('login') }}" class="btn btn-outline-warning btn-sm">Sign In</a>
@endif
Dengan begitu layout bisa dipakai sekarang, dan tautannya aktif sendiri begitu rutenya didaftarkan.
Pesan session('success') diisi controller saat redirect (misalnya setelah data tersimpan), jadi letakkan kotak pesannya di layout agar tampil di semua halaman. Lakukan hal yang sama untuk session('error').
Controller & Eager Loading
Buat controller dengan Artisan:
php artisan make:controller <NamaController>
Method index() mengambil data lewat Model lalu mengirimkannya ke view:
public function index()
{
$books = Book::with('author')->latest()->get();
return view('books.index', compact('books'));
}
with('author') adalah eager loading: data penulis semua buku diambil sekaligus dalam satu kueri tambahan. Tanpa with(…), setiap baris tabel yang memanggil $item->author memicu satu kueri baru — 100 buku berarti 101 kueri. Masalah ini disebut N+1 Query Problem. Jangan lupa baris use App\Models\Book; di atas controller.
Menampilkan Data di Blade
@forelse($books as $index => $item)
<tr>
<td>{{ $index + 1 }}</td>
<td>{{ $item->title }}</td>
<td>{{ $item->author->name }}</td>
<td>Rp {{ number_format($item->price, 0, ',', '.') }}</td>
<td>
@if($item->stock > 5)
<span class="badge bg-success">{{ $item->stock }} buku</span>
@elseif($item->stock > 0)
<span class="badge bg-warning text-dark">{{ $item->stock }} buku (menipis)</span>
@else
<span class="badge bg-danger">Habis</span>
@endif
</td>
</tr>
@empty
<tr>
<td colspan="5">Belum ada buku.</td>
</tr>
@endforelse
@forelse…@empty…@endforelsemengulang data, dan menampilkan bagian@emptybila datanya kosong.{{ $variabel }}mencetak nilai sekaligus meng-escape karakter HTML (lewathtmlspecialchars), sehingga tag<script>kiriman pengguna tampil sebagai teks dan tidak dijalankan (perlindungan XSS).number_format($angka, 0, ',', '.')mengubah15000menjadi15.000.@if/@elseif/@elsememilih badge berdasarkan sisa stok.
Tombol Edit memakai route('<nama>.edit', $item->id). Tombol Delete dibahas di Lab 6 karena memerlukan form khusus.
Hasil yang Diharapkan
Membuka http://127.0.0.1:8000 langsung mengarah ke halaman katalog produk bertema Bootstrap. Karena belum ada produk, tabel menampilkan pesan kosong dari bagian @empty. Produk pertama akan kamu tambahkan di Lab 4.