đŸ’ģ Pemrograman Web 2
🎓 Pertemuan
Pertemuan 11: Controller & View (MVC Pattern)

MODUL PEMROGRAMAN WEBSITE 2

Mata Kuliah: Pemrograman Website 2
Kode MK: INF2419
SKS: 3 (Praktikum)
Semester: Genap 2025/2026
Program Studi: Informatika
Fakultas: FEBI / Saintek
Universitas: UIN K.H. Abdurrahman Wahid Pekalongan

Dosen Pengampu: Mohammad Reza Maulana, M.Kom
NIP: 199110082025051002

Pertemuan: 11 dari 16
Durasi: 150 menit (3 × 50 menit)
Studi Kasus Berkelanjutan: Sistem Manajemen Perpustakaan


PERTEMUAN 11

CONTROLLER & VIEW (MVC PATTERN)

A. INFORMASI PERTEMUAN

AspekKeterangan
Capaian Pembelajaran Lulusan (CPL)CPL05: Memiliki pengetahuan memadai tentang cara kerja sistem komputer dan mampu merancang solusi algoritmik menggunakan framework.
Capaian Pembelajaran Mata Kuliah (CPMK)CPMK05.1: Mampu merancang struktur aplikasi web backend berbasis framework dengan pendekatan MVC.
Sub-CPMKSub-CPMK05.1.3: Mampu merancang routing, controller, model, dan view sesuai kebutuhan sistem.
Indikator PencapaianMahasiswa mampu:
1. Menjelaskan konsep dan tanggung jawab Controller dalam MVC
2. Membuat Controller untuk mengelola logika aplikasi
3. Memahami dan menggunakan routing advanced
4. Menguasai Blade templating engine
5. Passing data dari Controller ke View
6. Membuat layout master dan components
7. Menggunakan Blade directives (@foreach, @if, @extends, dll)
8. Menampilkan data database di View
Alokasi Waktuâ€ĸ Teori: 60 menit
â€ĸ Praktikum: 90 menit
â€ĸ Total: 150 menit (3 × 50 menit)

B. PENDAHULUAN

1. Deskripsi Singkat

Pertemuan kesebelas ini membahas implementasi lengkap pola MVC (Model-View-Controller) di Laravel 12. Fokus utama adalah pada Controller sebagai penghubung antara Model dan View, serta Blade templating engine untuk membuat tampilan yang dinamis dan reusable. Mahasiswa akan belajar bagaimana data dari database (Model) diproses oleh Controller dan ditampilkan ke user melalui View dengan Blade template.

2. Keterkaitan dengan Pertemuan Lain

Pertemuan ini merupakan penyatuan konsep-konsep yang telah dipelajari:

  • Pertemuan 9: Konsep MVC telah dijelaskan secara teori
  • Pertemuan 10: Model & Migration sudah dibuat, data sudah siap
  • Pertemuan 11: [SEKARANG] Implementasi Controller & View untuk menampilkan data
  • Pertemuan 12-13: CRUD lengkap menggunakan MVC pattern
  • Pertemuan 14-15: Authentication dan transaksi dengan MVC

Alur Pembelajaran MVC:

Pertemuan 9  → Pengenalan MVC (Konsep)
Pertemuan 10 → Model (Database Layer)
Pertemuan 11 → Controller & View (Logic & Presentation Layer)
Pertemuan 12 → CRUD Buku (Implementasi Lengkap MVC)

3. Manfaat Pembelajaran

  1. Memahami separation of concerns dalam aplikasi web
  2. Mampu membangun aplikasi dengan arsitektur yang terstruktur
  3. Dapat membuat tampilan yang reusable dengan Blade components
  4. Menguasai cara passing data dari backend ke frontend
  5. Siap mengimplementasikan CRUD operations

4. Relevansi dengan Studi Kasus

Dalam sistem perpustakaan, Controller & View digunakan untuk:

  • BukuController: Menangani logika tampilan daftar buku, detail buku
  • AnggotaController: Mengelola tampilan data anggota
  • Blade Layout: Template konsisten untuk semua halaman
  • Components: Navbar, sidebar, footer yang reusable
  • Directives: Loop untuk menampilkan multiple data, conditional untuk status

C. MATERI TEORI

1. Review: MVC Pattern

a. Apa itu MVC?

MVC (Model-View-Controller) adalah design pattern untuk memisahkan aplikasi menjadi 3 komponen utama:

┌─────────────────────────────────────────────────┐
│              MVC ARCHITECTURE                    │
├─────────────────────────────────────────────────┤
│                                                  │
│  ┌──────────┐      ┌──────────┐      ┌────────┐│
│  │  MODEL   │◄────â–ē│CONTROLLER│◄────â–ē│  VIEW  ││
│  └──────────┘      └──────────┘      └────────┘│
│       ▲                  ▲                 │    │
│       │                  │                 │    │
│  ┌────────┐         ┌────────┐       ┌────────┐│
│  │Database│         │ Logic  │       │  HTML  ││
│  │  Data  │         │Business│       │   CSS  ││
│  └────────┘         └────────┘       └────────┘│
│                                                  │
└─────────────────────────────────────────────────┘

Komponen MVC:

KomponenTanggung JawabFile Laravel
ModelInteraksi dengan database, business rulesapp/Models/Buku.php
ViewTampilan UI, presentasi dataresources/views/buku/index.blade.php
ControllerLogika aplikasi, penghubung Model-Viewapp/Http/Controllers/BukuController.php

b. Alur Kerja MVC di Laravel

User Request
    ↓
Route (web.php)
    ↓
Controller
    ↓
Model (Query Database)
    ↓
Controller (Process Data)
    ↓
View (Blade Template)
    ↓
Response HTML ke User

Contoh Konkret:

// 1. Route
Route::get('/buku', [BukuController::class, 'index']);
 
// 2. Controller
public function index() {
    $bukus = Buku::all();           // Model
    return view('buku.index', [     // View
        'bukus' => $bukus
    ]);
}
 
// 3. Model (sudah dibuat di Pertemuan 10)
class Buku extends Model { ... }
 
// 4. View (Blade)
@foreach($bukus as $buku)
    <h3>{{ $buku->judul }}</h3>
@endforeach

c. Mengapa MVC Penting?

Tanpa MVC (Spaghetti Code):

// Semua logic campur aduk dalam satu file
<?php
$conn = mysqli_connect(...);
$query = "SELECT * FROM buku";
$result = mysqli_query($conn, $query);
?>
<html>
<?php while($row = mysqli_fetch_assoc($result)): ?>
    <h3><?php echo $row['judul']; ?></h3>
<?php endwhile; ?>
</html>
<?php mysqli_close($conn); ?>

Dengan MVC (Clean & Organized):

// Controller
public function index() {
    $bukus = Buku::all();
    return view('buku.index', compact('bukus'));
}
 
// View
@foreach($bukus as $buku)
    <h3>{{ $buku->judul }}</h3>
@endforeach

Keuntungan MVC:

  • ✅ Separation of Concerns: Setiap komponen punya tugas jelas
  • ✅ Maintainability: Mudah maintenance dan update
  • ✅ Reusability: Component bisa dipakai ulang
  • ✅ Testability: Mudah untuk unit testing
  • ✅ Team Work: Developer bisa kerja parallel
  • ✅ Scalability: Mudah dikembangkan

2. Controller di Laravel

a. Apa itu Controller?

Controller adalah class yang menangani logika aplikasi. Controller menerima request, memproses data, dan mengembalikan response.

Tanggung Jawab Controller:

  1. Menerima input dari user (request)
  2. Validasi input
  3. Berinteraksi dengan Model (database)
  4. Memproses business logic
  5. Mengirim data ke View
  6. Mengembalikan response

Lokasi File: app/Http/Controllers/

b. Struktur Controller

<?php
 
namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
use App\Models\Buku;
 
class BukuController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index()
    {
        $bukus = Buku::all();
        return view('buku.index', compact('bukus'));
    }
 
    /**
     * Display the specified resource.
     */
    public function show(string $id)
    {
        $buku = Buku::findOrFail($id);
        return view('buku.show', compact('buku'));
    }
}

Penjelasan:

  • namespace App\Http\Controllers: Namespace controller
  • use App\Models\Buku: Import Model Buku
  • extends Controller: Inherit dari base Controller
  • public function index(): Method untuk list data
  • public function show($id): Method untuk detail data
  • compact('bukus'): Shorthand untuk passing variable ke view

c. Jenis-jenis Controller

1. Basic Controller:

class BukuController extends Controller
{
    public function index() { ... }
    public function show($id) { ... }
}

2. Resource Controller:

// Otomatis punya 7 methods:
// index, create, store, show, edit, update, destroy
php artisan make:controller BukuController --resource

3. Single Action Controller:

class ShowBukuController extends Controller
{
    public function __invoke($id)
    {
        return view('buku.show');
    }
}

4. API Resource Controller:

// Tanpa create dan edit (untuk API)
php artisan make:controller BukuController --api

d. Passing Data ke View

Laravel menyediakan beberapa cara untuk passing data:

1. Array Associative:

return view('buku.index', [
    'bukus' => $bukus,
    'title' => 'Daftar Buku'
]);

2. Compact Function:

$bukus = Buku::all();
$title = 'Daftar Buku';
return view('buku.index', compact('bukus', 'title'));

3. With Method:

return view('buku.index')
    ->with('bukus', $bukus)
    ->with('title', 'Daftar Buku');

4. With Array:

return view('buku.index')->with([
    'bukus' => $bukus,
    'title' => 'Daftar Buku'
]);

Best Practice: Gunakan compact() karena paling readable.


3. Routing Advanced

a. Review Basic Routing

// Basic GET route
Route::get('/buku', function () {
    return view('buku.index');
});
 
// Route dengan parameter
Route::get('/buku/{id}', function ($id) {
    return "Buku ID: $id";
});

b. Controller Routing

Method Syntax:

Route::get('/buku', [BukuController::class, 'index']);
Route::get('/buku/{id}', [BukuController::class, 'show']);

c. Resource Routing

Resource routing membuat semua route CRUD otomatis:

Route::resource('buku', BukuController::class);

Ini sama dengan:

Route::get('/buku', [BukuController::class, 'index'])->name('buku.index');
Route::get('/buku/create', [BukuController::class, 'create'])->name('buku.create');
Route::post('/buku', [BukuController::class, 'store'])->name('buku.store');
Route::get('/buku/{buku}', [BukuController::class, 'show'])->name('buku.show');
Route::get('/buku/{buku}/edit', [BukuController::class, 'edit'])->name('buku.edit');
Route::put('/buku/{buku}', [BukuController::class, 'update'])->name('buku.update');
Route::delete('/buku/{buku}', [BukuController::class, 'destroy'])->name('buku.destroy');

Tabel Resource Routes:

HTTP MethodURIActionRoute Name
GET/bukuindexbuku.index
GET/buku/createcreatebuku.create
POST/bukustorebuku.store
GET/buku/{id}showbuku.show
GET/buku/{id}/editeditbuku.edit
PUT/PATCH/buku/{id}updatebuku.update
DELETE/buku/{id}destroybuku.destroy

d. Route Naming

Named routes memudahkan generate URL:

// Define named route
Route::get('/buku', [BukuController::class, 'index'])->name('buku.index');
 
// Generate URL di Blade
<a href="{{ route('buku.index') }}">Daftar Buku</a>
 
// Redirect di Controller
return redirect()->route('buku.index');

e. Route Groups

Group by Prefix:

Route::prefix('admin')->group(function () {
    Route::get('/buku', [BukuController::class, 'index']);
    Route::get('/anggota', [AnggotaController::class, 'index']);
});
// URL: /admin/buku, /admin/anggota

Group by Middleware:

Route::middleware(['auth'])->group(function () {
    Route::resource('buku', BukuController::class);
    Route::resource('anggota', AnggotaController::class);
});

Group by Name:

Route::name('admin.')->group(function () {
    Route::get('/buku', [BukuController::class, 'index'])->name('buku.index');
    // Route name: admin.buku.index
});

f. Route Parameters

Required Parameters:

Route::get('/buku/{id}', [BukuController::class, 'show']);
 
// Controller
public function show($id) {
    $buku = Buku::findOrFail($id);
    return view('buku.show', compact('buku'));
}

Optional Parameters:

Route::get('/buku/{kategori?}', [BukuController::class, 'filter']);
 
// Controller
public function filter($kategori = null) {
    $query = Buku::query();
    if ($kategori) {
        $query->where('kategori', $kategori);
    }
    $bukus = $query->get();
    return view('buku.index', compact('bukus'));
}

Regular Expression Constraints:

// Hanya terima numeric
Route::get('/buku/{id}', [BukuController::class, 'show'])
     ->where('id', '[0-9]+');
 
// Multiple constraints
Route::get('/buku/{kategori}/{tahun}', [BukuController::class, 'filter'])
     ->where(['kategori' => '[A-Za-z]+', 'tahun' => '[0-9]{4}']);

g. Route Model Binding

Laravel otomatis inject Model berdasarkan ID:

// Automatic binding (by ID)
Route::get('/buku/{buku}', [BukuController::class, 'show']);
 
public function show(Buku $buku) {
    // Laravel otomatis find Buku by ID
    // Tidak perlu: $buku = Buku::findOrFail($id);
    return view('buku.show', compact('buku'));
}
 
// Custom binding (by field lain)
Route::get('/buku/{buku:kode_buku}', [BukuController::class, 'show']);

4. Blade Templating Engine

a. Apa itu Blade?

Blade adalah templating engine bawaan Laravel yang powerful dan mudah digunakan.

Keuntungan Blade:

  • ✅ Syntax sederhana dan intuitif
  • ✅ Template inheritance (layouts)
  • ✅ Components & slots
  • ✅ Directives untuk control structures
  • ✅ Raw PHP tetap bisa digunakan
  • ✅ Template caching otomatis
  • ✅ XSS protection built-in

Lokasi: resources/views/

Extension: .blade.php

b. Blade Syntax Basics

Menampilkan Data:

{{-- Escaped (safe dari XSS) --}}
<h1>{{ $title }}</h1>
<p>{{ $buku->judul }}</p>
 
{{-- Unescaped (raw HTML) --}}
<div>{!! $content !!}</div>
 
{{-- Dengan default value --}}
<p>{{ $description ?? 'Tidak ada deskripsi' }}</p>

Comments:

{{-- Ini komentar Blade (tidak muncul di HTML) --}}
 
<!-- Ini komentar HTML (muncul di source) -->

Displaying Raw Text:

@verbatim
    <div>{{ $variable }}</div>
@endverbatim
{{-- Output: <div>{{ $variable }}</div> --}}

c. Blade Directives

1. Conditional Directives:

{{-- @if, @elseif, @else --}}
@if ($buku->stok > 0)
    <span class="badge bg-success">Tersedia</span>
@elseif ($buku->stok == 0)
    <span class="badge bg-danger">Habis</span>
@else
    <span class="badge bg-warning">Stok Tidak Valid</span>
@endif
 
{{-- @unless (kebalikan if) --}}
@unless ($buku->stok == 0)
    <button>Pinjam</button>
@endunless
 
{{-- @isset, @empty --}}
@isset($buku->isbn)
    <p>ISBN: {{ $buku->isbn }}</p>
@endisset
 
@empty($buku->deskripsi)
    <p>Tidak ada deskripsi</p>
@endempty
 
{{-- @auth, @guest --}}
@auth
    <a href="/logout">Logout</a>
@endauth
 
@guest
    <a href="/login">Login</a>
@endguest

2. Loop Directives:

{{-- @foreach --}}
@foreach ($bukus as $buku)
    <div class="card">
        <h3>{{ $buku->judul }}</h3>
        <p>{{ $buku->pengarang }}</p>
    </div>
@endforeach
 
{{-- @forelse (foreach dengan else) --}}
@forelse ($bukus as $buku)
    <div>{{ $buku->judul }}</div>
@empty
    <p>Tidak ada data buku</p>
@endforelse
 
{{-- @for --}}
@for ($i = 0; $i < 10; $i++)
    <p>Nomor: {{ $i }}</p>
@endfor
 
{{-- @while --}}
@while (true)
    <p>Loop</p>
    @break
@endwhile
 
{{-- @continue, @break --}}
@foreach ($bukus as $buku)
    @if ($buku->stok == 0)
        @continue
    @endif
    <div>{{ $buku->judul }}</div>
    @if ($loop->index >= 10)
        @break
    @endif
@endforeach

3. Loop Variable ($loop):

@foreach ($bukus as $buku)
    @if ($loop->first)
        <div>Buku Pertama</div>
    @endif
 
    <div class="{{ $loop->even ? 'bg-gray' : 'bg-white' }}">
        {{ $loop->iteration }}. {{ $buku->judul }}
    </div>
 
    @if ($loop->last)
        <div>Buku Terakhir</div>
    @endif
@endforeach

$loop Properties:

  • $loop->index: Index (mulai dari 0)
  • $loop->iteration: Iteration (mulai dari 1)
  • $loop->remaining: Sisa iteration
  • $loop->count: Total items
  • $loop->first: Boolean, item pertama?
  • $loop->last: Boolean, item terakhir?
  • $loop->even: Boolean, index genap?
  • $loop->odd: Boolean, index ganjil?
  • $loop->depth: Nesting level
  • $loop->parent: Parent loop variable

d. Template Inheritance

Master Layout:

File: resources/views/layouts/app.blade.php

<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@yield('title') - Perpustakaan</title>
    
    {{-- CSS --}}
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
    @stack('styles')
</head>
<body>
    {{-- Navbar --}}
    @include('layouts.navbar')
 
    {{-- Main Content --}}
    <main class="container mt-4">
        @yield('content')
    </main>
 
    {{-- Footer --}}
    @include('layouts.footer')
 
    {{-- JavaScript --}}
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
    @stack('scripts')
</body>
</html>

Child View:

File: resources/views/buku/index.blade.php

@extends('layouts.app')
 
@section('title', 'Daftar Buku')
 
@push('styles')
<style>
    .card-custom { border: 2px solid #007bff; }
</style>
@endpush
 
@section('content')
    <h1>Daftar Buku</h1>
    
    <div class="row">
        @foreach ($bukus as $buku)
            <div class="col-md-4">
                <div class="card">
                    <div class="card-body">
                        <h5>{{ $buku->judul }}</h5>
                        <p>{{ $buku->pengarang }}</p>
                    </div>
                </div>
            </div>
        @endforeach
    </div>
@endsection
 
@push('scripts')
<script>
    console.log('Buku page loaded');
</script>
@endpush

Directives:

  • @extends('layouts.app'): Inherit dari layout
  • @section('content'): Define section content
  • @yield('content'): Placeholder untuk section
  • @include('layouts.navbar'): Include partial view
  • @push('scripts'): Push ke stack
  • @stack('scripts'): Output stack

e. Components

Creating Component:

php artisan make:component Alert

File: app/View/Components/Alert.php

<?php
 
namespace App\View\Components;
 
use Illuminate\View\Component;
 
class Alert extends Component
{
    public $type;
    public $message;
 
    public function __construct($type = 'info', $message = '')
    {
        $this->type = $type;
        $this->message = $message;
    }
 
    public function render()
    {
        return view('components.alert');
    }
}

File: resources/views/components/alert.blade.php

<div class="alert alert-{{ $type }} alert-dismissible fade show" role="alert">
    {{ $message }}
    <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>

Usage:

<x-alert type="success" message="Data berhasil disimpan!" />
<x-alert type="danger" message="Terjadi kesalahan!" />

5. Best Practices MVC Laravel

a. Controller Best Practices

✅ DO:

// 1. Skinny Controller - Logic di Model atau Service
public function index()
{
    $bukus = Buku::aktif()->latest()->get();
    return view('buku.index', compact('bukus'));
}
 
// 2. Single Responsibility
public function destroy($id)
{
    $buku = Buku::findOrFail($id);
    $buku->delete();
    return redirect()->route('buku.index')
                     ->with('success', 'Buku berhasil dihapus');
}
 
// 3. Validasi di Form Request (akan dipelajari pertemuan 12)
public function store(StoreBukuRequest $request)
{
    Buku::create($request->validated());
    return redirect()->route('buku.index');
}

❌ DON'T:

// 1. Fat Controller - terlalu banyak logic
public function index()
{
    $bukus = DB::table('buku')
        ->select('*')
        ->where('stok', '>', 0)
        ->orderBy('created_at', 'desc')
        ->get();
    
    $formatted = [];
    foreach ($bukus as $buku) {
        $formatted[] = [
            'judul' => $buku->judul,
            'harga_format' => 'Rp ' . number_format($buku->harga),
            // ... banyak processing
        ];
    }
    
    return view('buku.index', ['bukus' => $formatted]);
}
 
// 2. Query di View
return view('buku.index'); // View langsung query Buku::all()

b. View Best Practices

✅ DO:

{{-- 1. Gunakan layout inheritance --}}
@extends('layouts.app')
 
{{-- 2. Extract reusable components --}}
@include('partials.buku-card', ['buku' => $buku])
 
{{-- 3. Use Blade directives --}}
@forelse ($bukus as $buku)
    <div>{{ $buku->judul }}</div>
@empty
    <p>Tidak ada data</p>
@endforelse

❌ DON'T:

{{-- 1. Jangan query di view --}}
@php
    $bukus = \App\Models\Buku::all();
@endphp
 
{{-- 2. Jangan business logic di view --}}
@php
    $total = 0;
    foreach ($bukus as $buku) {
        $total += $buku->harga * $buku->stok;
    }
@endphp

c. Routing Best Practices

✅ DO:

// 1. Gunakan resource routes
Route::resource('buku', BukuController::class);
 
// 2. Route grouping
Route::middleware(['auth'])->group(function () {
    Route::resource('buku', BukuController::class);
    Route::resource('anggota', AnggotaController::class);
});
 
// 3. Named routes
Route::get('/buku', [BukuController::class, 'index'])->name('buku.index');

❌ DON'T:

// 1. Closure yang kompleks di route
Route::get('/buku', function () {
    // Puluhan baris code
    $bukus = Buku::where(...)
                 ->with(...)
                 ->orderBy(...)
                 ->paginate(10);
    return view('buku.index', compact('bukus'));
});

D. PRAKTIKUM

1. Tujuan Praktikum

  1. Membuat Controller untuk mengelola data buku
  2. Setup routing untuk buku
  3. Membuat layout master dengan Blade
  4. Membuat view untuk menampilkan list buku
  5. Membuat view untuk detail buku
  6. Menggunakan Blade directives
  7. Passing data dari Controller ke View

2. PRAKTIKUM 1: Membuat Blade Layout Master

Tujuan

Membuat template master yang akan digunakan semua halaman.

Langkah-langkah

a. Buat Folder Structure

cd resources/views
mkdir layouts
mkdir partials
mkdir buku
mkdir anggota

b. Buat Layout Master

File: resources/views/layouts/app.blade.php

<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>@yield('title', 'Perpustakaan') - Sistem Perpustakaan</title>
    
    {{-- Bootstrap CSS --}}
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
    
    {{-- Bootstrap Icons --}}
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
    
    {{-- Custom CSS --}}
    <style>
        body {
            min-height: 100vh;
            display: flex;
            flex-direction: column;
        }
        
        main {
            flex: 1;
        }
        
        .navbar-brand {
            font-weight: bold;
            font-size: 1.3rem;
        }
        
        footer {
            margin-top: auto;
            background-color: #f8f9fa;
            padding: 2rem 0;
        }
    </style>
    
    @stack('styles')
</head>
<body>
    {{-- Navbar --}}
    @include('layouts.navbar')
    
    {{-- Main Content --}}
    <main class="py-4">
        <div class="container">
            {{-- Alert Messages --}}
            @if (session('success'))
                <div class="alert alert-success alert-dismissible fade show" role="alert">
                    <i class="bi bi-check-circle-fill"></i>
                    {{ session('success') }}
                    <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
                </div>
            @endif
            
            @if (session('error'))
                <div class="alert alert-danger alert-dismissible fade show" role="alert">
                    <i class="bi bi-exclamation-triangle-fill"></i>
                    {{ session('error') }}
                    <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
                </div>
            @endif
            
            @if (session('info'))
                <div class="alert alert-info alert-dismissible fade show" role="alert">
                    <i class="bi bi-info-circle-fill"></i>
                    {{ session('info') }}
                    <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
                </div>
            @endif
            
            {{-- Page Content --}}
            @yield('content')
        </div>
    </main>
    
    {{-- Footer --}}
    @include('layouts.footer')
    
    {{-- Bootstrap JS --}}
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
    
    @stack('scripts')
</body>
</html>

c. Buat Navbar Component

File: resources/views/layouts/navbar.blade.php

<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
    <div class="container">
        <a class="navbar-brand" href="{{ url('/') }}">
            <i class="bi bi-book-fill"></i>
            Perpustakaan
        </a>
        
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
            <span class="navbar-toggler-icon"></span>
        </button>
        
        <div class="collapse navbar-collapse" id="navbarNav">
            <ul class="navbar-nav ms-auto">
                <li class="nav-item">
                    <a class="nav-link {{ Request::is('/') ? 'active' : '' }}" href="{{ url('/') }}">
                        <i class="bi bi-house-door"></i> Home
                    </a>
                </li>
                <li class="nav-item">
                    <a class="nav-link {{ Request::is('buku*') ? 'active' : '' }}" href="{{ route('buku.index') }}">
                        <i class="bi bi-book"></i> Buku
                    </a>
                </li>
                <li class="nav-item">
                    <a class="nav-link {{ Request::is('anggota*') ? 'active' : '' }}" href="{{ route('anggota.index') }}">
                        <i class="bi bi-people"></i> Anggota
                    </a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="#">
                        <i class="bi bi-arrow-left-right"></i> Transaksi
                    </a>
                </li>
            </ul>
        </div>
    </div>
</nav>

d. Buat Footer Component

File: resources/views/layouts/footer.blade.php

<footer class="bg-light border-top">
    <div class="container">
        <div class="row py-4">
            <div class="col-md-6">
                <h5><i class="bi bi-book-fill text-primary"></i> Sistem Perpustakaan</h5>
                <p class="text-muted mb-0">
                    Sistem Manajemen Perpustakaan menggunakan Laravel 12
                </p>
            </div>
            <div class="col-md-3">
                <h6>Menu</h6>
                <ul class="list-unstyled">
                    <li><a href="{{ url('/') }}" class="text-decoration-none">Home</a></li>
                    <li><a href="{{ route('buku.index') }}" class="text-decoration-none">Buku</a></li>
                    <li><a href="{{ route('anggota.index') }}" class="text-decoration-none">Anggota</a></li>
                </ul>
            </div>
            <div class="col-md-3">
                <h6>Kontak</h6>
                <p class="text-muted small mb-0">
                    <i class="bi bi-envelope"></i> perpustakaan@example.com<br />
                    <i class="bi bi-telephone"></i> (021) 1234-5678
                </p>
            </div>
        </div>
        <div class="row border-top pt-3">
            <div class="col text-center text-muted small">
                <p class="mb-0">
                    &copy; {{ date('Y') }} Sistem Perpustakaan. 
                    Built with <i class="bi bi-heart-fill text-danger"></i> using Laravel 12.
                </p>
            </div>
        </div>
    </div>
</footer>

e. Test Layout - Buat Home Page

File: resources/views/home.blade.php

@extends('layouts.app')
 
@section('title', 'Home')
 
@section('content')
<div class="text-center py-5">
    <h1 class="display-4">
        <i class="bi bi-book-fill text-primary"></i>
        Selamat Datang di Sistem Perpustakaan
    </h1>
    <p class="lead text-muted">
        Sistem manajemen perpustakaan modern menggunakan Laravel 12
    </p>
    
    <div class="row mt-5">
        <div class="col-md-4">
            <div class="card border-primary">
                <div class="card-body text-center">
                    <i class="bi bi-book display-1 text-primary"></i>
                    <h5 class="card-title mt-3">Kelola Buku</h5>
                    <p class="card-text">Manajemen koleksi buku perpustakaan</p>
                    <a href="{{ route('buku.index') }}" class="btn btn-primary">
                        Lihat Buku <i class="bi bi-arrow-right"></i>
                    </a>
                </div>
            </div>
        </div>
        
        <div class="col-md-4">
            <div class="card border-success">
                <div class="card-body text-center">
                    <i class="bi bi-people display-1 text-success"></i>
                    <h5 class="card-title mt-3">Kelola Anggota</h5>
                    <p class="card-text">Manajemen data anggota perpustakaan</p>
                    <a href="{{ route('anggota.index') }}" class="btn btn-success">
                        Lihat Anggota <i class="bi bi-arrow-right"></i>
                    </a>
                </div>
            </div>
        </div>
        
        <div class="col-md-4">
            <div class="card border-info">
                <div class="card-body text-center">
                    <i class="bi bi-arrow-left-right display-1 text-info"></i>
                    <h5 class="card-title mt-3">Transaksi</h5>
                    <p class="card-text">Peminjaman dan pengembalian buku</p>
                    <a href="#" class="btn btn-info text-white">
                        Transaksi <i class="bi bi-arrow-right"></i>
                    </a>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

f. Setup Route Home

Edit routes/web.php:

<?php
 
use Illuminate\Support\Facades\Route;
 
Route::get('/', function () {
    return view('home');
})->name('home');

g. Testing

  1. Jalankan server: php artisan serve
  2. Akses: http://localhost:8000
  3. Verifikasi:
    • Navbar muncul dengan benar
    • Footer muncul di bawah
    • Card untuk Buku, Anggota, Transaksi ada
    • Layout responsive (test di mobile view)

3. PRAKTIKUM 2: Membuat BukuController

Tujuan

Membuat controller untuk mengelola tampilan data buku.

Langkah-langkah

a. Generate Controller

php artisan make:controller BukuController --resource

Output:

Controller created successfully.

File ter-generate di: app/Http/Controllers/BukuController.php

b. Edit BukuController

File: app/Http/Controllers/BukuController.php

<?php
 
namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
use App\Models\Buku;
 
class BukuController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index()
    {
        // Ambil semua data buku dari database
        $bukus = Buku::latest()->get();
        
        // Statistik untuk card
        $totalBuku = Buku::count();
        $bukuTersedia = Buku::where('stok', '>', 0)->count();
        $bukuHabis = Buku::where('stok', 0)->count();
        
        // Return view dengan data
        return view('buku.index', compact(
            'bukus',
            'totalBuku',
            'bukuTersedia',
            'bukuHabis'
        ));
    }
 
    /**
     * Show the form for creating a new resource.
     */
    public function create()
    {
        // Akan diimplementasi di pertemuan 12
        return view('buku.create');
    }
 
    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request)
    {
        // Akan diimplementasi di pertemuan 12
    }
 
    /**
     * Display the specified resource.
     */
    public function show(string $id)
    {
        // Find buku by ID, throw 404 if not found
        $buku = Buku::findOrFail($id);
        
        // Return view detail buku
        return view('buku.show', compact('buku'));
    }
 
    /**
     * Show the form for editing the specified resource.
     */
    public function edit(string $id)
    {
        // Akan diimplementasi di pertemuan 12
        $buku = Buku::findOrFail($id);
        return view('buku.edit', compact('buku'));
    }
 
    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, string $id)
    {
        // Akan diimplementasi di pertemuan 12
    }
 
    /**
     * Remove the specified resource from storage.
     */
    public function destroy(string $id)
    {
        // Akan diimplementasi di pertemuan 12
    }
    
    /**
     * Filter buku berdasarkan kategori.
     */
    public function filterKategori($kategori)
    {
        $bukus = Buku::where('kategori', $kategori)->latest()->get();
        
        $totalBuku = $bukus->count();
        $bukuTersedia = $bukus->where('stok', '>', 0)->count();
        $bukuHabis = $bukus->where('stok', 0)->count();
        
        return view('buku.index', compact(
            'bukus',
            'totalBuku',
            'bukuTersedia',
            'bukuHabis',
            'kategori'
        ));
    }
}

c. Setup Routes

Edit routes/web.php:

<?php
 
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\BukuController;
use App\Http\Controllers\AnggotaController;
 
Route::get('/', function () {
    return view('home');
})->name('home');
 
// Resource route untuk Buku
Route::resource('buku', BukuController::class);
 
// Custom route untuk filter kategori
Route::get('/buku/kategori/{kategori}', [BukuController::class, 'filterKategori'])
     ->name('buku.kategori');
 
// Resource route untuk Anggota (akan dibuat nanti)
Route::resource('anggota', AnggotaController::class);

d. Test Routes

php artisan route:list --name=buku

Output:

GET|HEAD   buku ................ buku.index â€ē BukuController@index
GET|HEAD   buku/create ......... buku.create â€ē BukuController@create
POST       buku ................ buku.store â€ē BukuController@store
GET|HEAD   buku/{buku} ......... buku.show â€ē BukuController@show
GET|HEAD   buku/{buku}/edit .... buku.edit â€ē BukuController@edit
PUT|PATCH  buku/{buku} ......... buku.update â€ē BukuController@update
DELETE     buku/{buku} ......... buku.destroy â€ē BukuController@destroy
GET|HEAD   buku/kategori/{kategori} buku.kategori â€ē BukuController@filterKategori

4. PRAKTIKUM 3: View Daftar Buku (Index)

Tujuan

Membuat halaman untuk menampilkan daftar semua buku.

Langkah-langkah

a. Buat View Index

File: resources/views/buku/index.blade.php

@extends('layouts.app')
 
@section('title', 'Daftar Buku')
 
@section('content')
<div class="d-flex justify-content-between align-items-center mb-4">
    <h1>
        <i class="bi bi-book"></i>
        Daftar Buku
    </h1>
    <a href="{{ route('buku.create') }}" class="btn btn-primary">
        <i class="bi bi-plus-circle"></i> Tambah Buku
    </a>
</div>
 
{{-- Statistik Cards --}}
<div class="row mb-4">
    <div class="col-md-4">
        <div class="card border-primary">
            <div class="card-body">
                <div class="d-flex justify-content-between align-items-center">
                    <div>
                        <h6 class="text-muted mb-1">Total Buku</h6>
                        <h2 class="mb-0">{{ $totalBuku }}</h2>
                    </div>
                    <div class="text-primary">
                        <i class="bi bi-book-fill" style="font-size: 3rem;"></i>
                    </div>
                </div>
            </div>
        </div>
    </div>
    
    <div class="col-md-4">
        <div class="card border-success">
            <div class="card-body">
                <div class="d-flex justify-content-between align-items-center">
                    <div>
                        <h6 class="text-muted mb-1">Buku Tersedia</h6>
                        <h2 class="mb-0">{{ $bukuTersedia }}</h2>
                    </div>
                    <div class="text-success">
                        <i class="bi bi-check-circle-fill" style="font-size: 3rem;"></i>
                    </div>
                </div>
            </div>
        </div>
    </div>
    
    <div class="col-md-4">
        <div class="card border-danger">
            <div class="card-body">
                <div class="d-flex justify-content-between align-items-center">
                    <div>
                        <h6 class="text-muted mb-1">Buku Habis</h6>
                        <h2 class="mb-0">{{ $bukuHabis }}</h2>
                    </div>
                    <div class="text-danger">
                        <i class="bi bi-x-circle-fill" style="font-size: 3rem;"></i>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
 
{{-- Filter Kategori --}}
<div class="card mb-4">
    <div class="card-body">
        <h6 class="card-title">
            <i class="bi bi-funnel"></i> Filter Kategori:
        </h6>
        <div class="btn-group" role="group">
            <a href="{{ route('buku.index') }}" class="btn btn-sm {{ !isset($kategori) ? 'btn-primary' : 'btn-outline-primary' }}">
                Semua
            </a>
            <a href="{{ route('buku.kategori', 'Programming') }}" class="btn btn-sm {{ isset($kategori) && $kategori == 'Programming' ? 'btn-primary' : 'btn-outline-primary' }}">
                Programming
            </a>
            <a href="{{ route('buku.kategori', 'Database') }}" class="btn btn-sm {{ isset($kategori) && $kategori == 'Database' ? 'btn-primary' : 'btn-outline-primary' }}">
                Database
            </a>
            <a href="{{ route('buku.kategori', 'Web Design') }}" class="btn btn-sm {{ isset($kategori) && $kategori == 'Web Design' ? 'btn-primary' : 'btn-outline-primary' }}">
                Web Design
            </a>
            <a href="{{ route('buku.kategori', 'Networking') }}" class="btn btn-sm {{ isset($kategori) && $kategori == 'Networking' ? 'btn-primary' : 'btn-outline-primary' }}">
                Networking
            </a>
            <a href="{{ route('buku.kategori', 'Data Science') }}" class="btn btn-sm {{ isset($kategori) && $kategori == 'Data Science' ? 'btn-primary' : 'btn-outline-primary' }}">
                Data Science
            </a>
        </div>
    </div>
</div>
 
{{-- Daftar Buku --}}
@forelse ($bukus as $buku)
    <div class="card mb-3">
        <div class="card-body">
            <div class="row">
                <div class="col-md-2 text-center">
                    <i class="bi bi-book text-primary" style="font-size: 4rem;"></i>
                    <div class="mt-2">
                        <span class="badge bg-{{ $buku->kategori == 'Programming' ? 'primary' : ($buku->kategori == 'Database' ? 'success' : ($buku->kategori == 'Web Design' ? 'info' : ($buku->kategori == 'Networking' ? 'warning' : 'danger'))) }}">
                            {{ $buku->kategori }}
                        </span>
                    </div>
                </div>
                
                <div class="col-md-7">
                    <h5 class="card-title">
                        <a href="{{ route('buku.show', $buku->id) }}" class="text-decoration-none">
                            {{ $buku->judul }}
                        </a>
                    </h5>
                    
                    <p class="card-text text-muted mb-2">
                        <i class="bi bi-person"></i> {{ $buku->pengarang }} | 
                        <i class="bi bi-building"></i> {{ $buku->penerbit }} | 
                        <i class="bi bi-calendar"></i> {{ $buku->tahun_terbit }}
                    </p>
                    
                    @if ($buku->isbn)
                        <p class="card-text small text-muted mb-1">
                            <i class="bi bi-upc"></i> ISBN: {{ $buku->isbn }}
                        </p>
                    @endif
                    
                    @if ($buku->deskripsi)
                        <p class="card-text">
                            {{ Str::limit($buku->deskripsi, 150) }}
                        </p>
                    @endif
                </div>
                
                <div class="col-md-3 text-end">
                    <h4 class="text-primary mb-2">
                        {{ $buku->harga_format }}
                    </h4>
                    
                    <div class="mb-3">
                        @if ($buku->stok > 0)
                            <span class="badge bg-success">
                                <i class="bi bi-check-circle"></i> Tersedia
                            </span>
                            <div class="text-muted small mt-1">
                                Stok: {{ $buku->stok }} buku
                            </div>
                        @else
                            <span class="badge bg-danger">
                                <i class="bi bi-x-circle"></i> Habis
                            </span>
                        @endif
                    </div>
                    
                    <div class="btn-group-vertical d-grid gap-2">
                        <a href="{{ route('buku.show', $buku->id) }}" class="btn btn-sm btn-info text-white">
                            <i class="bi bi-eye"></i> Detail
                        </a>
                        <a href="{{ route('buku.edit', $buku->id) }}" class="btn btn-sm btn-warning">
                            <i class="bi bi-pencil"></i> Edit
                        </a>
                    </div>
                </div>
            </div>
        </div>
    </div>
@empty
    <div class="alert alert-info">
        <i class="bi bi-info-circle"></i>
        Tidak ada data buku
        @isset($kategori)
            dengan kategori <strong>{{ $kategori }}</strong>
        @endisset
    </div>
@endforelse
 
@if ($bukus->count() > 0)
    <div class="text-center mt-4">
        <p class="text-muted">
            Menampilkan {{ $bukus->count() }} buku
            @isset($kategori)
                dari kategori <strong>{{ $kategori }}</strong>
            @endisset
        </p>
    </div>
@endif
@endsection

b. Test View

  1. Akses: http://localhost:8000/buku
  2. Verifikasi:
    • Card statistik muncul
    • Filter kategori berfungsi
    • Daftar buku tampil dengan card
    • Badge kategori dengan warna berbeda
    • Status stok (tersedia/habis) muncul
    • Button Detail dan Edit ada

c. Test Filter

  1. Klik filter "Programming"
  2. URL berubah ke: /buku/kategori/Programming
  3. Hanya buku kategori Programming yang muncul
  4. Test filter kategori lainnya

5. PRAKTIKUM 4: View Detail Buku (Show)

Tujuan

Membuat halaman detail buku yang menampilkan informasi lengkap.

Langkah-langkah

a. Buat View Show

File: resources/views/buku/show.blade.php

@extends('layouts.app')
 
@section('title', $buku->judul)
 
@section('content')
<div class="row">
    {{-- Breadcrumb --}}
    <div class="col-12 mb-3">
        <nav aria-label="breadcrumb">
            <ol class="breadcrumb">
                <li class="breadcrumb-item"><a href="{{ route('home') }}">Home</a></li>
                <li class="breadcrumb-item"><a href="{{ route('buku.index') }}">Buku</a></li>
                <li class="breadcrumb-item active">{{ $buku->judul }}</li>
            </ol>
        </nav>
    </div>
</div>
 
<div class="row">
    {{-- Kolom Kiri: Info Buku --}}
    <div class="col-md-8">
        <div class="card">
            <div class="card-header bg-primary text-white">
                <h4 class="mb-0">
                    <i class="bi bi-book"></i>
                    Detail Buku
                </h4>
            </div>
            <div class="card-body">
                {{-- Judul --}}
                <h2 class="mb-3">{{ $buku->judul }}</h2>
                
                {{-- Badge Kategori --}}
                <div class="mb-3">
                    <span class="badge bg-{{ $buku->kategori == 'Programming' ? 'primary' : ($buku->kategori == 'Database' ? 'success' : ($buku->kategori == 'Web Design' ? 'info' : ($buku->kategori == 'Networking' ? 'warning' : 'danger'))) }} fs-6">
                        <i class="bi bi-tag"></i> {{ $buku->kategori }}
                    </span>
                </div>
                
                {{-- Informasi Detail --}}
                <table class="table table-borderless">
                    <tr>
                        <td width="200" class="fw-bold">
                            <i class="bi bi-upc text-primary"></i> Kode Buku
                        </td>
                        <td>: {{ $buku->kode_buku }}</td>
                    </tr>
                    <tr>
                        <td class="fw-bold">
                            <i class="bi bi-person text-primary"></i> Pengarang
                        </td>
                        <td>: {{ $buku->pengarang }}</td>
                    </tr>
                    <tr>
                        <td class="fw-bold">
                            <i class="bi bi-building text-primary"></i> Penerbit
                        </td>
                        <td>: {{ $buku->penerbit }}</td>
                    </tr>
                    <tr>
                        <td class="fw-bold">
                            <i class="bi bi-calendar text-primary"></i> Tahun Terbit
                        </td>
                        <td>: {{ $buku->tahun_terbit }}</td>
                    </tr>
                    @if ($buku->isbn)
                        <tr>
                            <td class="fw-bold">
                                <i class="bi bi-hash text-primary"></i> ISBN
                            </td>
                            <td>: {{ $buku->isbn }}</td>
                        </tr>
                    @endif
                    <tr>
                        <td class="fw-bold">
                            <i class="bi bi-translate text-primary"></i> Bahasa
                        </td>
                        <td>: {{ $buku->bahasa }}</td>
                    </tr>
                    <tr>
                        <td class="fw-bold">
                            <i class="bi bi-cash text-primary"></i> Harga
                        </td>
                        <td>: <span class="text-success fs-5 fw-bold">{{ $buku->harga_format }}</span></td>
                    </tr>
                    <tr>
                        <td class="fw-bold">
                            <i class="bi bi-boxes text-primary"></i> Stok
                        </td>
                        <td>
                            : <span class="fw-bold">{{ $buku->stok }}</span> buku
                            @if ($buku->stok > 0)
                                <span class="badge bg-success ms-2">
                                    <i class="bi bi-check-circle"></i> Tersedia
                                </span>
                            @else
                                <span class="badge bg-danger ms-2">
                                    <i class="bi bi-x-circle"></i> Habis
                                </span>
                            @endif
                        </td>
                    </tr>
                </table>
                
                {{-- Deskripsi --}}
                @if ($buku->deskripsi)
                    <hr>
                    <h5><i class="bi bi-file-text text-primary"></i> Deskripsi</h5>
                    <p class="text-justify">{{ $buku->deskripsi }}</p>
                @else
                    <hr>
                    <p class="text-muted fst-italic">
                        <i class="bi bi-info-circle"></i> Tidak ada deskripsi untuk buku ini
                    </p>
                @endif
                
                {{-- Timestamps --}}
                <hr>
                <div class="row text-muted small">
                    <div class="col-md-6">
                        <i class="bi bi-clock"></i> 
                        Ditambahkan: {{ $buku->created_at->format('d M Y H:i') }}
                    </div>
                    <div class="col-md-6 text-end">
                        <i class="bi bi-clock-history"></i> 
                        Terakhir Update: {{ $buku->updated_at->format('d M Y H:i') }}
                    </div>
                </div>
            </div>
        </div>
    </div>
    
    {{-- Kolom Kanan: Actions & Info Tambahan --}}
    <div class="col-md-4">
        {{-- Card Actions --}}
        <div class="card mb-3">
            <div class="card-header bg-secondary text-white">
                <h6 class="mb-0">
                    <i class="bi bi-gear"></i> Aksi
                </h6>
            </div>
            <div class="card-body d-grid gap-2">
                <a href="{{ route('buku.edit', $buku->id) }}" class="btn btn-warning">
                    <i class="bi bi-pencil"></i> Edit Buku
                </a>
                
                @if ($buku->stok > 0)
                    <button class="btn btn-success">
                        <i class="bi bi-cart-plus"></i> Pinjam Buku
                    </button>
                @else
                    <button class="btn btn-secondary" disabled>
                        <i class="bi bi-x-circle"></i> Stok Habis
                    </button>
                @endif
                
                <a href="{{ route('buku.index') }}" class="btn btn-outline-primary">
                    <i class="bi bi-arrow-left"></i> Kembali
                </a>
                
                <hr>
                
                <form action="{{ route('buku.destroy', $buku->id) }}" method="POST" onsubmit="return confirm('Yakin ingin menghapus buku ini?')">
                    @csrf
                    @method('DELETE')
                    <button type="submit" class="btn btn-danger w-100">
                        <i class="bi bi-trash"></i> Hapus Buku
                    </button>
                </form>
            </div>
        </div>
        
        {{-- Card Status Stok --}}
        <div class="card mb-3">
            <div class="card-header bg-info text-white">
                <h6 class="mb-0">
                    <i class="bi bi-info-circle"></i> Status Stok
                </h6>
            </div>
            <div class="card-body">
                @if ($buku->stok == 0)
                    <div class="alert alert-danger mb-0">
                        <i class="bi bi-exclamation-triangle"></i>
                        <strong>Stok Habis!</strong><br />
                        Buku ini sedang tidak tersedia.
                    </div>
                @elseif ($buku->stok <= 5)
                    <div class="alert alert-warning mb-0">
                        <i class="bi bi-exclamation-circle"></i>
                        <strong>Stok Menipis!</strong><br />
                        Tersisa {{ $buku->stok }} buku.
                    </div>
                @else
                    <div class="alert alert-success mb-0">
                        <i class="bi bi-check-circle"></i>
                        <strong>Stok Aman!</strong><br />
                        Tersedia {{ $buku->stok }} buku.
                    </div>
                @endif
            </div>
        </div>
        
        {{-- Card Buku Serupa --}}
        <div class="card">
            <div class="card-header bg-dark text-white">
                <h6 class="mb-0">
                    <i class="bi bi-collection"></i> Buku Serupa
                </h6>
            </div>
            <div class="card-body">
                @php
                    $bukuSerupa = App\Models\Buku::where('kategori', $buku->kategori)
                                                  ->where('id', '!=', $buku->id)
                                                  ->take(3)
                                                  ->get();
                @endphp
                
                @forelse ($bukuSerupa as $item)
                    <div class="mb-3">
                        <a href="{{ route('buku.show', $item->id) }}" class="text-decoration-none">
                            <h6 class="mb-1">{{ Str::limit($item->judul, 40) }}</h6>
                        </a>
                        <small class="text-muted">{{ $item->pengarang }}</small>
                    </div>
                    @if (!$loop->last)
                        <hr>
                    @endif
                @empty
                    <p class="text-muted small mb-0">
                        <i class="bi bi-info-circle"></i>
                        Tidak ada buku serupa
                    </p>
                @endforelse
            </div>
        </div>
    </div>
</div>
@endsection

b. Test View

  1. Dari halaman index, klik "Detail" pada salah satu buku
  2. URL: http://localhost:8000/buku/1
  3. Verifikasi:
    • Breadcrumb muncul
    • Informasi lengkap buku tampil
    • Badge kategori dengan warna
    • Status stok dengan alert
    • Button action (Edit, Pinjam, Hapus, Kembali)
    • Card buku serupa muncul

6. PRAKTIKUM 5: Membuat AnggotaController & View

Tujuan

Membuat controller dan view untuk data anggota dengan konsep yang sama.

Langkah-langkah

a. Generate AnggotaController

php artisan make:controller AnggotaController --resource

b. Edit AnggotaController

File: app/Http/Controllers/AnggotaController.php

<?php
 
namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
use App\Models\Anggota;
 
class AnggotaController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index()
    {
        $anggotas = Anggota::latest()->get();
        
        // Statistik
        $totalAnggota = Anggota::count();
        $anggotaAktif = Anggota::where('status', 'Aktif')->count();
        $anggotaNonaktif = Anggota::where('status', 'Nonaktif')->count();
        
        return view('anggota.index', compact(
            'anggotas',
            'totalAnggota',
            'anggotaAktif',
            'anggotaNonaktif'
        ));
    }
 
    /**
     * Display the specified resource.
     */
    public function show(string $id)
    {
        $anggota = Anggota::findOrFail($id);
        return view('anggota.show', compact('anggota'));
    }
 
    // Methods lainnya akan diimplementasi di pertemuan 13
    public function create() { }
    public function store(Request $request) { }
    public function edit(string $id) { }
    public function update(Request $request, string $id) { }
    public function destroy(string $id) { }
}

c. Buat View Index Anggota

File: resources/views/anggota/index.blade.php

@extends('layouts.app')
 
@section('title', 'Daftar Anggota')
 
@section('content')
<div class="d-flex justify-content-between align-items-center mb-4">
    <h1>
        <i class="bi bi-people"></i>
        Daftar Anggota
    </h1>
    <a href="{{ route('anggota.create') }}" class="btn btn-success">
        <i class="bi bi-plus-circle"></i> Tambah Anggota
    </a>
</div>
 
{{-- Statistik --}}
<div class="row mb-4">
    <div class="col-md-4">
        <div class="card border-success">
            <div class="card-body">
                <div class="d-flex justify-content-between">
                    <div>
                        <h6 class="text-muted">Total Anggota</h6>
                        <h2>{{ $totalAnggota }}</h2>
                    </div>
                    <i class="bi bi-people-fill text-success" style="font-size: 3rem;"></i>
                </div>
            </div>
        </div>
    </div>
    <div class="col-md-4">
        <div class="card border-primary">
            <div class="card-body">
                <div class="d-flex justify-content-between">
                    <div>
                        <h6 class="text-muted">Anggota Aktif</h6>
                        <h2>{{ $anggotaAktif }}</h2>
                    </div>
                    <i class="bi bi-person-check-fill text-primary" style="font-size: 3rem;"></i>
                </div>
            </div>
        </div>
    </div>
    <div class="col-md-4">
        <div class="card border-secondary">
            <div class="card-body">
                <div class="d-flex justify-content-between">
                    <div>
                        <h6 class="text-muted">Anggota Nonaktif</h6>
                        <h2>{{ $anggotaNonaktif }}</h2>
                    </div>
                    <i class="bi bi-person-x-fill text-secondary" style="font-size: 3rem;"></i>
                </div>
            </div>
        </div>
    </div>
</div>
 
{{-- Tabel Anggota --}}
<div class="card">
    <div class="card-body">
        <div class="table-responsive">
            <table class="table table-hover">
                <thead class="table-light">
                    <tr>
                        <th>No</th>
                        <th>Kode</th>
                        <th>Nama</th>
                        <th>Email</th>
                        <th>Telepon</th>
                        <th>Jenis Kelamin</th>
                        <th>Status</th>
                        <th>Aksi</th>
                    </tr>
                </thead>
                <tbody>
                    @forelse ($anggotas as $anggota)
                        <tr>
                            <td>{{ $loop->iteration }}</td>
                            <td>
                                <code>{{ $anggota->kode_anggota }}</code>
                            </td>
                            <td>
                                <strong>{{ $anggota->nama }}</strong>
                            </td>
                            <td>
                                <i class="bi bi-envelope"></i>
                                {{ $anggota->email }}
                            </td>
                            <td>
                                <i class="bi bi-telephone"></i>
                                {{ $anggota->telepon }}
                            </td>
                            <td>
                                @if ($anggota->jenis_kelamin == 'Laki-laki')
                                    <i class="bi bi-gender-male text-primary"></i>
                                @else
                                    <i class="bi bi-gender-female text-danger"></i>
                                @endif
                                {{ $anggota->jenis_kelamin }}
                            </td>
                            <td>
                                @if ($anggota->status == 'Aktif')
                                    <span class="badge bg-success">
                                        <i class="bi bi-check-circle"></i> Aktif
                                    </span>
                                @else
                                    <span class="badge bg-secondary">
                                        <i class="bi bi-x-circle"></i> Nonaktif
                                    </span>
                                @endif
                            </td>
                            <td>
                                <div class="btn-group" role="group">
                                    <a href="{{ route('anggota.show', $anggota->id) }}" 
                                       class="btn btn-sm btn-info text-white"
                                       title="Detail">
                                        <i class="bi bi-eye"></i>
                                    </a>
                                    <a href="{{ route('anggota.edit', $anggota->id) }}" 
                                       class="btn btn-sm btn-warning"
                                       title="Edit">
                                        <i class="bi bi-pencil"></i>
                                    </a>
                                </div>
                            </td>
                        </tr>
                    @empty
                        <tr>
                            <td colspan="8" class="text-center text-muted">
                                <i class="bi bi-inbox"></i>
                                Tidak ada data anggota
                            </td>
                        </tr>
                    @endforelse
                </tbody>
            </table>
        </div>
    </div>
</div>
@endsection

d. Buat View Show Anggota

File: resources/views/anggota/show.blade.php

@extends('layouts.app')
 
@section('title', $anggota->nama)
 
@section('content')
<div class="row">
    <div class="col-12 mb-3">
        <nav aria-label="breadcrumb">
            <ol class="breadcrumb">
                <li class="breadcrumb-item"><a href="{{ route('home') }}">Home</a></li>
                <li class="breadcrumb-item"><a href="{{ route('anggota.index') }}">Anggota</a></li>
                <li class="breadcrumb-item active">{{ $anggota->nama }}</li>
            </ol>
        </nav>
    </div>
</div>
 
<div class="row">
    <div class="col-md-8">
        <div class="card">
            <div class="card-header bg-success text-white">
                <h4 class="mb-0">
                    <i class="bi bi-person"></i>
                    Detail Anggota
                </h4>
            </div>
            <div class="card-body">
                <div class="text-center mb-4">
                    @if ($anggota->jenis_kelamin == 'Laki-laki')
                        <i class="bi bi-person-circle text-primary" style="font-size: 5rem;"></i>
                    @else
                        <i class="bi bi-person-circle text-danger" style="font-size: 5rem;"></i>
                    @endif
                    <h3 class="mt-2">{{ $anggota->nama }}</h3>
                    @if ($anggota->status == 'Aktif')
                        <span class="badge bg-success">
                            <i class="bi bi-check-circle"></i> Anggota Aktif
                        </span>
                    @else
                        <span class="badge bg-secondary">
                            <i class="bi bi-x-circle"></i> Nonaktif
                        </span>
                    @endif
                </div>
                
                <table class="table table-borderless">
                    <tr>
                        <td width="200" class="fw-bold">
                            <i class="bi bi-upc text-success"></i> Kode Anggota
                        </td>
                        <td>: <code>{{ $anggota->kode_anggota }}</code></td>
                    </tr>
                    <tr>
                        <td class="fw-bold">
                            <i class="bi bi-envelope text-success"></i> Email
                        </td>
                        <td>: {{ $anggota->email }}</td>
                    </tr>
                    <tr>
                        <td class="fw-bold">
                            <i class="bi bi-telephone text-success"></i> Telepon
                        </td>
                        <td>: {{ $anggota->telepon }}</td>
                    </tr>
                    <tr>
                        <td class="fw-bold">
                            <i class="bi bi-geo-alt text-success"></i> Alamat
                        </td>
                        <td>: {{ $anggota->alamat }}</td>
                    </tr>
                    <tr>
                        <td class="fw-bold">
                            <i class="bi bi-calendar text-success"></i> Tanggal Lahir
                        </td>
                        <td>: {{ $anggota->tanggal_lahir->format('d F Y') }} ({{ $anggota->umur }} tahun)</td>
                    </tr>
                    <tr>
                        <td class="fw-bold">
                            <i class="bi bi-gender-ambiguous text-success"></i> Jenis Kelamin
                        </td>
                        <td>: {{ $anggota->jenis_kelamin }}</td>
                    </tr>
                    <tr>
                        <td class="fw-bold">
                            <i class="bi bi-briefcase text-success"></i> Pekerjaan
                        </td>
                        <td>: {{ $anggota->pekerjaan ?? '-' }}</td>
                    </tr>
                    <tr>
                        <td class="fw-bold">
                            <i class="bi bi-calendar-check text-success"></i> Tanggal Daftar
                        </td>
                        <td>: {{ $anggota->tanggal_daftar->format('d F Y') }} ({{ $anggota->lama_anggota }} hari)</td>
                    </tr>
                </table>
                
                <hr>
                <div class="row text-muted small">
                    <div class="col-md-6">
                        <i class="bi bi-clock"></i> 
                        Ditambahkan: {{ $anggota->created_at->format('d M Y H:i') }}
                    </div>
                    <div class="col-md-6 text-end">
                        <i class="bi bi-clock-history"></i> 
                        Terakhir Update: {{ $anggota->updated_at->format('d M Y H:i') }}
                    </div>
                </div>
            </div>
        </div>
    </div>
    
    <div class="col-md-4">
        <div class="card mb-3">
            <div class="card-header bg-secondary text-white">
                <h6 class="mb-0">
                    <i class="bi bi-gear"></i> Aksi
                </h6>
            </div>
            <div class="card-body d-grid gap-2">
                <a href="{{ route('anggota.edit', $anggota->id) }}" class="btn btn-warning">
                    <i class="bi bi-pencil"></i> Edit Anggota
                </a>
                <a href="{{ route('anggota.index') }}" class="btn btn-outline-success">
                    <i class="bi bi-arrow-left"></i> Kembali
                </a>
                <hr>
                <form action="{{ route('anggota.destroy', $anggota->id) }}" method="POST" onsubmit="return confirm('Yakin ingin menghapus?')">
                    @csrf
                    @method('DELETE')
                    <button type="submit" class="btn btn-danger w-100">
                        <i class="bi bi-trash"></i> Hapus Anggota
                    </button>
                </form>
            </div>
        </div>
    </div>
</div>
@endsection

e. Test AnggotaController

  1. Akses: http://localhost:8000/anggota
  2. Verifikasi halaman index anggota
  3. Klik detail salah satu anggota
  4. Verifikasi halaman show anggota

E. TUGAS

Tugas 1: Membuat Halaman Dashboard (30%)

Instruksi: Buat halaman dashboard yang menampilkan ringkasan statistik perpustakaan.

Spesifikasi:

  1. Controller: Buat DashboardController dengan method index()
  2. Route: /dashboard
  3. Data yang ditampilkan:
    • Total buku, buku tersedia, buku habis
    • Total anggota, anggota aktif, anggota nonaktif
    • List 5 buku terbaru
    • List 5 anggota terbaru
    • Quick links ke menu utama

Tugas 2: Blade Component untuk Card Buku (40%)

Instruksi: Buat Blade Component reusable untuk menampilkan card buku.

Spesifikasi:

  1. Generate Component:
php artisan make:component BukuCard
  1. Component Properties:

    • $buku (object Buku)
    • $showActions (boolean, default true)
  2. Component Design:

    • Tampilkan: cover (icon), judul, pengarang, harga, stok
    • Badge kategori
    • Status ketersediaan
    • Button actions (Detail, Edit) jika $showActions = true

Tugas 3: Search & Filter Buku Advanced (30%)

Instruksi: Tambahkan fitur pencarian dan filter advanced untuk buku.

Spesifikasi:

  1. Form Search:

    • Input keyword (search judul, pengarang, penerbit)
    • Filter kategori (dropdown)
    • Filter tahun (dropdown)
    • Filter ketersediaan (Semua/Tersedia/Habis)
  2. Controller Method di BukuController:

public function search(Request $request)
{
    $query = Buku::query();
    
    // Filter implementation
    
    $bukus = $query->latest()->get();
    return view('buku.index', compact('bukus'));
}
  1. Route: /buku/search

Submission:

  • Format: Link repository GitHub (sertakan screenshot di README)
  • Deadline: Pertemuan 12
  • Upload ke: Ngaji UIN Gusdur (submit link repository GitHub)

F. EVALUASI

1. Kuis Singkat

Pilihan Ganda:

  1. Di MVC, komponen yang menangani logika aplikasi adalah:

    • A. Model
    • B. View
    • C. Controller
    • D. Router
  2. Method controller untuk menampilkan detail data adalah:

    • A. index()
    • B. show()
    • C. detail()
    • D. display()
  3. Blade directive untuk looping data adalah:

    • A. @loop
    • B. @for
    • C. @foreach
    • D. Semua benar
  4. Cara passing data ke view dengan compact:

    • A. compact('data')
    • B. compact($data)
    • C. compact->data
    • D. compact(['data'])
  5. Directive untuk extend layout adalah:

    • A. @include
    • B. @extends
    • C. @layout
    • D. @inherit
  6. Cara menampilkan data di Blade (escaped):

    • A. <?php echo $data ?>
    • B. {!! $data !!}
    • C. {{ $data }}
    • D. @echo $data
  7. Named route dibuat dengan:

    • A. Route::name('nama')
    • B. ->name('nama')
    • C. ->named('nama')
    • D. as => 'nama'
  8. Resource routing membuat berapa routes:

    • A. 5
    • B. 6
    • C. 7
    • D. 8
  9. $loop->first digunakan untuk:

    • A. Item pertama loop
    • B. Loop pertama
    • C. Index pertama
    • D. First data
  10. Cara include partial view:

    • A. @include('path.file')
    • B. @partial('path.file')
    • C. @import('path.file')
    • D. @require('path.file')

Essay:

  1. Jelaskan alur kerja MVC di Laravel ketika user mengakses /buku! (15 poin)

  2. Apa perbedaan @yield dan @section di Blade? Berikan contoh! (10 poin)

  3. Sebutkan 5 Blade directives dan fungsinya! (10 poin)

  4. Buatlah route resource untuk TransaksiController! (10 poin)

  5. Apa keuntungan menggunakan layout inheritance di Blade? (10 poin)


2. Checklist Kompetensi

NoKompetensiBelumCukupMahir
1Memahami konsep MVC○○○
2Membuat Controller○○○
3Setup routing○○○
4Passing data ke View○○○
5Menggunakan Blade directives○○○
6Layout inheritance○○○
7Blade components○○○
8Named routes○○○
9Query data dari Model○○○
10Menampilkan data di View○○○

G. REFERENSI

1. Dokumentasi Laravel 12

2. Bootstrap 5

3. Tutorial Laravel

  • Laravel Daily - Controllers Best Practices
  • Laracasts - Blade Mastery
  • Laravel News - MVC Pattern

H. PERSIAPAN PERTEMUAN 12

Topik: CRUD Buku dengan Laravel

Preview:

  1. Form create & validation
  2. Store data ke database
  3. Form edit & update
  4. Delete data
  5. Flash messages
  6. Form Request validation

Yang Perlu Disiapkan:

  1. Controller & View sudah jalan
  2. Pahami form handling
  3. Study validation rules Laravel
  4. Siapkan browser untuk testing

Pre-reading:


Selamat Belajar! 🚀đŸ’ģ

End of Module - Pertemuan 11

Next: Pertemuan 12 - CRUD Buku dengan Laravel