WebTech Interaktif XI RPL

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

  1. Buka routes/web.php, ganti seluruh isinya: arahkan / ke halaman indeks produk dan daftarkan resource route untuk produk (beserta use controllernya).
  2. Buat folder resources/views/layouts/, lalu buat layouts/app.blade.php berisi kerangka HTML, Bootstrap 5.3 dan Bootstrap Icons, navbar (tautan katalog, tautan transaksi, status login), kotak pesan success/error, dan @yield('content').
  3. Jalankan php artisan make:controller <NamaController> untuk controller produk, lalu isi method index() yang mengambil produk beserta kategorinya (eager loading, urut terbaru) dan mengirimkannya ke view.
  4. Buat folder resources/views/<folder>/ untuk produk, lalu buat index.blade.php berisi tabel katalog: nomor, kode, nama, kategori, harga berformat rupiah, badge stok, serta tombol Edit dan Delete.
  5. Buka http://127.0.0.1:8000 dan 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 HTTPURINama ruteMethod controller
GET/booksbooks.indexindex — daftar data
GET/books/createbooks.createcreate — form tambah
POST/booksbooks.storestore — simpan data baru
GET/books/{book}books.showshow — detail
GET/books/{book}/editbooks.editedit — form ubah
PUT/PATCH/books/{book}books.updateupdate — simpan perubahan
DELETE/books/{book}books.destroydestroy — 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 … @endforelse mengulang data, dan menampilkan bagian @empty bila datanya kosong.
  • {{ $variabel }} mencetak nilai sekaligus meng-escape karakter HTML (lewat htmlspecialchars), sehingga tag <script> kiriman pengguna tampil sebagai teks dan tidak dijalankan (perlindungan XSS).
  • number_format($angka, 0, ',', '.') mengubah 15000 menjadi 15.000.
  • @if / @elseif / @else memilih 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.