HARI 2: Routing, Controllers, dan Views
Bootcamp Laravel - Sistem Pengelolaan Dokumen (SiDoku)
📋 Informasi Sesi
| Item | Keterangan |
|---|---|
| Hari | 2 dari 5 |
| Durasi | 8 Jam (09:00 - 17:00 WIB) |
| Topik Utama | Routing, Controllers, Views (Blade) |
| Project | SiDoku - Membuat halaman dasar |
🎯 Learning Objectives
Setelah menyelesaikan sesi ini, peserta akan mampu:
- Memahami sistem routing di Laravel
- Membuat dan menggunakan Controller
- Menggunakan Blade Templating Engine
- Mengirim data dari Controller ke View
- Membuat layout dan component Blade
- Mengintegrasikan Tailwind CSS
SESI 1: Routing di Laravel
1.1 Pengenalan Routing
Routing adalah mekanisme untuk menentukan bagaimana aplikasi merespons request ke URL tertentu.
File routing utama:
routes/web.php- Route untuk web browserroutes/api.php- Route untuk API
1.2 Basic Routes
Buka routes/web.php:
<?php
use Illuminate\Support\Facades\Route;
// Route dasar - return string
Route::get('/', function () {
return 'Selamat Datang di SiDoku!';
});
// Route dengan view
Route::get('/home', function () {
return view('home');
});
// Route dengan parameter
Route::get('/dokumen/{id}', function ($id) {
return "Menampilkan dokumen dengan ID: $id";
});
// Route dengan parameter opsional
Route::get('/user/{name?}', function ($name = 'Guest') {
return "Halo, $name!";
});1.3 Route Methods
// GET - Mengambil data
Route::get('/dokumen', function () {
return 'Daftar dokumen';
});
// POST - Mengirim data baru
Route::post('/dokumen', function () {
return 'Dokumen disimpan';
});
// PUT/PATCH - Update data
Route::put('/dokumen/{id}', function ($id) {
return "Dokumen $id diupdate";
});
// DELETE - Hapus data
Route::delete('/dokumen/{id}', function ($id) {
return "Dokumen $id dihapus";
});
// Match multiple methods
Route::match(['get', 'post'], '/form', function () {
return 'Form handler';
});
// Any method
Route::any('/webhook', function () {
return 'Webhook received';
});1.4 Named Routes
// Memberi nama pada route
Route::get('/dokumen', function () {
return view('dokumen.index');
})->name('dokumen.index');
Route::get('/dokumen/{id}', function ($id) {
return view('dokumen.show', ['id' => $id]);
})->name('dokumen.show');
// Menggunakan named route di view
// route('dokumen.index') => /dokumen
// route('dokumen.show', ['id' => 5]) => /dokumen/51.5 Route Groups
// Group dengan prefix
Route::prefix('admin')->group(function () {
Route::get('/dashboard', function () {
return 'Admin Dashboard';
}); // URL: /admin/dashboard
Route::get('/users', function () {
return 'Manage Users';
}); // URL: /admin/users
});
// Group dengan middleware
Route::middleware(['auth'])->group(function () {
Route::get('/profile', function () {
return 'Profile Page';
});
Route::get('/settings', function () {
return 'Settings Page';
});
});1.6 Route Parameter Constraints
// Constraint dengan regex
Route::get('/dokumen/{id}', function ($id) {
return "Dokumen ID: $id";
})->where('id', '[0-9]+');
// Multiple constraints
Route::get('/user/{id}/{name}', function ($id, $name) {
return "User $name (ID: $id)";
})->where([
'id' => '[0-9]+',
'name' => '[a-zA-Z]+'
]);
// Global constraint di RouteServiceProvider
// whereNumber('id')
// whereAlpha('name')
// whereAlphaNumeric('code')SESI 2: Controllers
2.1 Membuat Controller
Menggunakan Artisan:
php artisan make:controller DokumenControllerHasil: app/Http/Controllers/DokumenController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class DokumenController extends Controller
{
//
}2.2 Controller dengan Methods
Edit DokumenController.php:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class DokumenController extends Controller
{
// Menampilkan daftar dokumen
public function index()
{
$dokumen = [
['id' => 1, 'judul' => 'SK Mutasi', 'nomor' => 'SK/001/2025'],
['id' => 2, 'judul' => 'Nota Dinas', 'nomor' => 'ND/015/2025'],
['id' => 3, 'judul' => 'Surat Tugas', 'nomor' => 'ST/022/2025'],
];
return view('dokumen.index', compact('dokumen'));
}
// Menampilkan form tambah
public function create()
{
return view('dokumen.create');
}
// Menyimpan dokumen baru
public function store(Request $request)
{
// Logic simpan ke database
return redirect()->route('dokumen.index')
->with('success', 'Dokumen berhasil ditambahkan!');
}
// Menampilkan detail dokumen
public function show($id)
{
$dokumen = [
'id' => $id,
'judul' => 'SK Mutasi',
'nomor' => 'SK/001/2025',
'tanggal' => '2025-06-15'
];
return view('dokumen.show', compact('dokumen'));
}
// Menampilkan form edit
public function edit($id)
{
$dokumen = [
'id' => $id,
'judul' => 'SK Mutasi',
'nomor' => 'SK/001/2025'
];
return view('dokumen.edit', compact('dokumen'));
}
// Update dokumen
public function update(Request $request, $id)
{
// Logic update
return redirect()->route('dokumen.index')
->with('success', 'Dokumen berhasil diupdate!');
}
// Hapus dokumen
public function destroy($id)
{
// Logic hapus
return redirect()->route('dokumen.index')
->with('success', 'Dokumen berhasil dihapus!');
}
}2.3 Menghubungkan Route ke Controller
Edit routes/web.php:
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\DokumenController;
// Route individual
Route::get('/dokumen', [DokumenController::class, 'index'])->name('dokumen.index');
Route::get('/dokumen/create', [DokumenController::class, 'create'])->name('dokumen.create');
Route::post('/dokumen', [DokumenController::class, 'store'])->name('dokumen.store');
Route::get('/dokumen/{id}', [DokumenController::class, 'show'])->name('dokumen.show');
Route::get('/dokumen/{id}/edit', [DokumenController::class, 'edit'])->name('dokumen.edit');
Route::put('/dokumen/{id}', [DokumenController::class, 'update'])->name('dokumen.update');
Route::delete('/dokumen/{id}', [DokumenController::class, 'destroy'])->name('dokumen.destroy');2.4 Resource Controller
Membuat controller dengan semua method CRUD:
php artisan make:controller KategoriController --resourceRoute Resource (lebih singkat):
Route::resource('kategori', KategoriController::class);
// PENTING untuk nama resource berbahasa Indonesia:
// Laravel mungkin membuat parameter {kategorium} atau salah
// Gunakan ->parameters() untuk memastikan nama parameter benar:
Route::resource('dokumen', DokumenController::class)
->parameters(['dokumen' => 'dokumen']);⚠️ TIP: Jika nama resource bukan bahasa Inggris standar, selalu gunakan
->parameters()untuk memastikan route model binding berfungsi dengan benar.
Ini otomatis membuat 7 route:
| Method | URI | Action | Route Name |
|---|---|---|---|
| GET | /kategori | index | kategori.index |
| GET | /kategori/create | create | kategori.create |
| POST | /kategori | store | kategori.store |
| GET | /kategori/ | show | kategori.show |
| GET | /kategori/{id}/edit | edit | kategori.edit |
| PUT/PATCH | /kategori/ | update | kategori.update |
| DELETE | /kategori/ | destroy | kategori.destroy |
2.5 Melihat Semua Routes
php artisan route:listOutput:
+--------+----------+------------------------+------------------+
| Method | URI | Name | Action |
+--------+----------+------------------------+------------------+
| GET | / | | Closure |
| GET | dokumen | dokumen.index | DokumenController|
| POST | dokumen | dokumen.store | DokumenController|
...SESI 3: Blade Templating Engine
3.1 Pengenalan Blade
Blade adalah templating engine bawaan Laravel dengan fitur:
- Syntax yang bersih dan mudah dibaca
- Template inheritance
- Components
- Caching otomatis
File Blade: resources/views/namafile.blade.php
3.2 Blade Basics
Buat resources/views/dokumen/index.blade.php:
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>Daftar Dokumen - SiDoku</title>
</head>
<body>
<h1>Daftar Dokumen</h1>
{{-- Ini adalah komentar Blade --}}
{{-- Menampilkan variabel (escaped) --}}
<p>Total: {{ count($dokumen) }} dokumen</p>
{{-- Menampilkan tanpa escape (raw HTML) --}}
{!! '<strong>Perhatian:</strong> Gunakan dengan hati-hati' !!}
{{-- Kondisi --}}
@if(count($dokumen) > 0)
<table border="1">
<tr>
<th>ID</th>
<th>Judul</th>
<th>Nomor</th>
</tr>
@foreach($dokumen as $dok)
<tr>
<td>{{ $dok['id'] }}</td>
<td>{{ $dok['judul'] }}</td>
<td>{{ $dok['nomor'] }}</td>
</tr>
@endforeach
</table>
@else
<p>Belum ada dokumen.</p>
@endif
</body>
</html>3.3 Direktif Kondisi
{{-- If-Else --}}
@if($user->isAdmin())
<p>Selamat datang, Admin!</p>
@elseif($user->isEditor())
<p>Selamat datang, Editor!</p>
@else
<p>Selamat datang, User!</p>
@endif
{{-- Unless (kebalikan if) --}}
@unless($dokumen->isApproved())
<span class="badge">Pending</span>
@endunless
{{-- Empty check --}}
@empty($dokumen)
<p>Tidak ada dokumen</p>
@endempty
{{-- Isset --}}
@isset($message)
<div class="alert">{{ $message }}</div>
@endisset
{{-- Switch --}}
@switch($status)
@case('draft')
<span class="badge-gray">Draft</span>
@break
@case('pending')
<span class="badge-yellow">Pending</span>
@break
@case('approved')
<span class="badge-green">Approved</span>
@break
@default
<span class="badge">Unknown</span>
@endswitch3.4 Direktif Loop
{{-- Foreach --}}
@foreach($dokumen as $dok)
<div>{{ $dok->judul }}</div>
@endforeach
{{-- Foreach dengan empty fallback --}}
@forelse($dokumen as $dok)
<div>{{ $dok->judul }}</div>
@empty
<p>Tidak ada dokumen</p>
@endforelse
{{-- For --}}
@for($i = 0; $i < 10; $i++)
<span>{{ $i }}</span>
@endfor
{{-- While --}}
@while(true)
<p>Infinite loop (jangan lakukan ini)</p>
@break
@endwhile
{{-- Loop variable --}}
@foreach($dokumen as $dok)
@if($loop->first)
<div class="first">Pertama</div>
@endif
<div>{{ $loop->iteration }}. {{ $dok->judul }}</div>
@if($loop->last)
<div class="last">Terakhir</div>
@endif
@endforeach$loop variable properties:
| Property | Keterangan |
|---|---|
$loop->index | Index (mulai 0) |
$loop->iteration | Iterasi (mulai 1) |
$loop->first | Iterasi pertama? |
$loop->last | Iterasi terakhir? |
$loop->count | Total items |
$loop->remaining | Sisa iterasi |
3.5 Template Inheritance (Layouts)
Langkah 1: Buat Layout
Buat 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', 'SiDoku')</title>
{{-- Tailwind CSS CDN --}}
<script src="https://cdn.tailwindcss.com"></script>
@stack('styles')
</head>
<body class="bg-gray-100">
{{-- Navbar --}}
<nav class="bg-blue-600 text-white p-4">
<div class="container mx-auto flex justify-between">
<a href="/" class="font-bold text-xl">SiDoku</a>
<div class="space-x-4">
<a href="{{ route('dokumen.index') }}">Dokumen</a>
<a href="#">Kategori</a>
<a href="#">Profil</a>
</div>
</div>
</nav>
{{-- Content --}}
<main class="container mx-auto py-8 px-4">
@yield('content')
</main>
{{-- Footer --}}
<footer class="bg-gray-800 text-white p-4 text-center">
<p>© 2025 SiDoku - Sistem Pengelolaan Dokumen</p>
</footer>
@stack('scripts')
</body>
</html>Langkah 2: Extend Layout
Edit resources/views/dokumen/index.blade.php:
@extends('layouts.app')
@section('title', 'Daftar Dokumen')
@section('content')
<div class="bg-white rounded-lg shadow p-6">
<div class="flex justify-between items-center mb-6">
<h1 class="text-2xl font-bold">Daftar Dokumen</h1>
<a href="{{ route('dokumen.create') }}"
class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">
+ Tambah Dokumen
</a>
</div>
{{-- Flash message --}}
@if(session('success'))
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded mb-4">
{{ session('success') }}
</div>
@endif
{{-- Table --}}
<table class="w-full border-collapse">
<thead>
<tr class="bg-gray-200">
<th class="border p-2 text-left">No</th>
<th class="border p-2 text-left">Judul</th>
<th class="border p-2 text-left">Nomor</th>
<th class="border p-2 text-left">Aksi</th>
</tr>
</thead>
<tbody>
@forelse($dokumen as $dok)
<tr class="hover:bg-gray-50">
<td class="border p-2">{{ $loop->iteration }}</td>
<td class="border p-2">{{ $dok['judul'] }}</td>
<td class="border p-2">{{ $dok['nomor'] }}</td>
<td class="border p-2">
<a href="{{ route('dokumen.show', $dok['id']) }}"
class="text-blue-500 hover:underline">Lihat</a>
<a href="{{ route('dokumen.edit', $dok['id']) }}"
class="text-yellow-500 hover:underline ml-2">Edit</a>
</td>
</tr>
@empty
<tr>
<td colspan="4" class="border p-4 text-center text-gray-500">
Belum ada dokumen
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@endsection3.6 Blade Components
Membuat Component:
php artisan make:component AlertHasil:
app/View/Components/Alert.php(class)resources/views/components/alert.blade.php(view)
Edit Component Class app/View/Components/Alert.php:
<?php
namespace App\View\Components;
use Closure;
use Illuminate\Contracts\View\View;
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 typeClass()
{
return match($this->type) {
'success' => 'bg-green-100 border-green-400 text-green-700',
'error' => 'bg-red-100 border-red-400 text-red-700',
'warning' => 'bg-yellow-100 border-yellow-400 text-yellow-700',
default => 'bg-blue-100 border-blue-400 text-blue-700',
};
}
public function render(): View|Closure|string
{
return view('components.alert');
}
}Edit View resources/views/components/alert.blade.php:
<div class="border px-4 py-3 rounded mb-4 {{ $typeClass() }}">
@if($message)
{{ $message }}
@else
{{ $slot }}
@endif
</div>Menggunakan Component:
{{-- Dengan message prop --}}
<x-alert type="success" message="Data berhasil disimpan!" />
{{-- Dengan slot --}}
<x-alert type="error">
Terjadi kesalahan saat menyimpan data.
</x-alert>
{{-- Default type --}}
<x-alert>
Ini adalah informasi.
</x-alert>3.7 Anonymous Components
Buat file langsung tanpa class:
resources/views/components/button.blade.php:
@props([
'type' => 'button',
'variant' => 'primary'
])
@php
$classes = match($variant) {
'primary' => 'bg-blue-500 hover:bg-blue-600 text-white',
'secondary' => 'bg-gray-500 hover:bg-gray-600 text-white',
'danger' => 'bg-red-500 hover:bg-red-600 text-white',
'success' => 'bg-green-500 hover:bg-green-600 text-white',
default => 'bg-blue-500 hover:bg-blue-600 text-white',
};
@endphp
<button type="{{ $type }}" {{ $attributes->merge(['class' => "px-4 py-2 rounded $classes"]) }}>
{{ $slot }}
</button>Penggunaan:
<x-button>Submit</x-button>
<x-button variant="danger">Hapus</x-button>
<x-button type="submit" variant="success">Simpan</x-button>SESI 4: Praktikum - Halaman SiDoku
Praktikum 1: Membuat Halaman Dashboard
Controller:
php artisan make:controller DashboardControllerEdit app/Http/Controllers/DashboardController.php:
<?php
namespace App\Http\Controllers;
class DashboardController extends Controller
{
public function index()
{
$stats = [
'total_dokumen' => 150,
'pending' => 12,
'approved' => 130,
'rejected' => 8,
];
$recent = [
['judul' => 'SK Mutasi Pegawai', 'tanggal' => '2025-06-20'],
['judul' => 'Nota Dinas Rapat', 'tanggal' => '2025-06-19'],
['judul' => 'Surat Tugas Diklat', 'tanggal' => '2025-06-18'],
];
return view('dashboard', compact('stats', 'recent'));
}
}Route:
use App\Http\Controllers\DashboardController;
Route::get('/', [DashboardController::class, 'index'])->name('dashboard');View resources/views/dashboard.blade.php:
@extends('layouts.app')
@section('title', 'Dashboard')
@section('content')
<h1 class="text-2xl font-bold mb-6">Dashboard</h1>
{{-- Stats Cards --}}
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-8">
<div class="bg-white rounded-lg shadow p-6">
<h3 class="text-gray-500 text-sm">Total Dokumen</h3>
<p class="text-3xl font-bold text-blue-600">{{ $stats['total_dokumen'] }}</p>
</div>
<div class="bg-white rounded-lg shadow p-6">
<h3 class="text-gray-500 text-sm">Pending</h3>
<p class="text-3xl font-bold text-yellow-600">{{ $stats['pending'] }}</p>
</div>
<div class="bg-white rounded-lg shadow p-6">
<h3 class="text-gray-500 text-sm">Approved</h3>
<p class="text-3xl font-bold text-green-600">{{ $stats['approved'] }}</p>
</div>
<div class="bg-white rounded-lg shadow p-6">
<h3 class="text-gray-500 text-sm">Rejected</h3>
<p class="text-3xl font-bold text-red-600">{{ $stats['rejected'] }}</p>
</div>
</div>
{{-- Recent Documents --}}
<div class="bg-white rounded-lg shadow p-6">
<h2 class="text-xl font-bold mb-4">Dokumen Terbaru</h2>
<ul class="divide-y">
@foreach($recent as $dok)
<li class="py-3 flex justify-between">
<span>{{ $dok['judul'] }}</span>
<span class="text-gray-500">{{ $dok['tanggal'] }}</span>
</li>
@endforeach
</ul>
</div>
@endsectionPraktikum 2: Form Tambah Dokumen
Buat resources/views/dokumen/create.blade.php:
@extends('layouts.app')
@section('title', 'Tambah Dokumen')
@section('content')
<div class="max-w-2xl mx-auto">
<div class="bg-white rounded-lg shadow p-6">
<h1 class="text-2xl font-bold mb-6">Tambah Dokumen Baru</h1>
<form action="{{ route('dokumen.store') }}" method="POST" enctype="multipart/form-data">
@csrf
{{-- Judul --}}
<div class="mb-4">
<label class="block text-gray-700 font-medium mb-2">Judul Dokumen</label>
<input type="text"
name="judul"
value="{{ old('judul') }}"
class="w-full border rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="Masukkan judul dokumen">
@error('judul')
<p class="text-red-500 text-sm mt-1">{{ $message }}</p>
@enderror
</div>
{{-- Nomor Dokumen --}}
<div class="mb-4">
<label class="block text-gray-700 font-medium mb-2">Nomor Dokumen</label>
<input type="text"
name="nomor"
value="{{ old('nomor') }}"
class="w-full border rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="SK/001/2025">
</div>
{{-- Kategori --}}
<div class="mb-4">
<label class="block text-gray-700 font-medium mb-2">Kategori</label>
<select name="kategori" class="w-full border rounded px-3 py-2">
<option value="">-- Pilih Kategori --</option>
<option value="sk">Surat Keputusan</option>
<option value="nd">Nota Dinas</option>
<option value="st">Surat Tugas</option>
<option value="lain">Lainnya</option>
</select>
</div>
{{-- Deskripsi --}}
<div class="mb-4">
<label class="block text-gray-700 font-medium mb-2">Deskripsi</label>
<textarea name="deskripsi"
rows="4"
class="w-full border rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="Deskripsi dokumen...">{{ old('deskripsi') }}</textarea>
</div>
{{-- File Upload --}}
<div class="mb-6">
<label class="block text-gray-700 font-medium mb-2">Upload File</label>
<input type="file"
name="file"
class="w-full border rounded px-3 py-2"
accept=".pdf,.doc,.docx">
<p class="text-gray-500 text-sm mt-1">Format: PDF, DOC, DOCX. Maks: 2MB</p>
</div>
{{-- Buttons --}}
<div class="flex justify-end space-x-2">
<a href="{{ route('dokumen.index') }}"
class="px-4 py-2 bg-gray-300 rounded hover:bg-gray-400">
Batal
</a>
<button type="submit"
class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">
Simpan
</button>
</div>
</form>
</div>
</div>
@endsectionPraktikum 3: Halaman Detail Dokumen
Buat resources/views/dokumen/show.blade.php:
@extends('layouts.app')
@section('title', 'Detail Dokumen')
@section('content')
<div class="max-w-3xl mx-auto">
<div class="bg-white rounded-lg shadow p-6">
<div class="flex justify-between items-start mb-6">
<div>
<h1 class="text-2xl font-bold">{{ $dokumen['judul'] }}</h1>
<p class="text-gray-500">{{ $dokumen['nomor'] }}</p>
</div>
<span class="px-3 py-1 bg-green-100 text-green-800 rounded-full text-sm">
Approved
</span>
</div>
<div class="grid grid-cols-2 gap-4 mb-6">
<div>
<h3 class="text-gray-500 text-sm">Tanggal</h3>
<p>{{ $dokumen['tanggal'] }}</p>
</div>
<div>
<h3 class="text-gray-500 text-sm">Kategori</h3>
<p>Surat Keputusan</p>
</div>
<div>
<h3 class="text-gray-500 text-sm">Pembuat</h3>
<p>Admin</p>
</div>
<div>
<h3 class="text-gray-500 text-sm">Terakhir Diupdate</h3>
<p>{{ $dokumen['tanggal'] }}</p>
</div>
</div>
<div class="border-t pt-4">
<h3 class="font-medium mb-2">Deskripsi</h3>
<p class="text-gray-700">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</p>
</div>
<div class="flex justify-end space-x-2 mt-6">
<a href="{{ route('dokumen.index') }}"
class="px-4 py-2 bg-gray-300 rounded hover:bg-gray-400">
Kembali
</a>
<a href="{{ route('dokumen.edit', $dokumen['id']) }}"
class="px-4 py-2 bg-yellow-500 text-white rounded hover:bg-yellow-600">
Edit
</a>
</div>
</div>
</div>
@endsection✅ Checkpoint Hari 2
Pastikan Anda sudah bisa:
- Membuat berbagai jenis route
- Membuat Controller dan menghubungkan dengan route
- Menggunakan Blade templating
- Membuat layout dan menggunakan @extends
- Membuat dan menggunakan Blade components
- Menampilkan data dari Controller ke View
Selamat! Anda siap untuk Hari 3: Database dan Eloquent ORM!