Inventaris Lab Laravel 12 : Bagian #07 - MEMBUAT HALAMAN CRUD CATEGORY
Belajar Bareng Minarsih - Edisi Ngoding
09 Oktober 2025
Tujuan: Selamat datang di bagian ini! Setelah berhasil membuat CRUD Users dari nol dan membuat fitur Autentikasi untuk Pengguna bisa Login, sekarang kita akan membangun CRUD untuk Category dengan cara yang lebih cepat dan efisien. Di bagian ini, kita akan fokus pada teknik refactoring (mengadaptasi kode yang sudah ada) untuk mempercepat proses development, sambil tetap memperhatikan setiap detail perubahan.
Kita akan mulai dari "belakang", yaitu menyiapkan "jalan" (Route) dan "otak" (Controller) untuk fitur Category.
use App\Http\Controllers\CategoryController;
Route::middleware('auth')->group(function () {
// ... route dashboard dan users
Route::resource('category', CategoryController::class);
});
php artisan route:list --name=category
Sekarang, kita akan mengisi logika pada controller untuk mengambil data semua kategori dari database dan menampilkannya.
use App\Models\Category;
class CategoryController extends Controller
{
// Variabel untuk data umum
protected $title = 'Category';
protected $menu = 'category';
protected $directory = 'admin.category'; // Diubah ke folder view category
public function index()
{
// Menyiapkan array untuk dikirim ke view
$data['title'] = $this->title;
$data['menu'] = $this->menu;
// Mengambil data dari database
$data['categories'] = Category::latest()->get();
// Me-return view beserta data
return view($this->directory . '.index', $data);
}
Penjelasan Kode: Baris $data['categories'] = Category::latest()->get(); memiliki arti:
Daripada membuat file tampilan dari nol, kita akan menduplikasi folder users yang sudah lengkap, karena struktur halamannya akan sangat mirip.
Sekarang adalah bagian terpenting: mengubah file index.blade.php di dalam folder category agar sesuai dengan data kategori.
@extends('admin.layouts.app')
@section('css')
{{-- CSS Tambahan --}}
@endsection
@section('content')
<div class="card">
<div class="card-body">
<h5 class="card-title fw-semibold mb-4">Data {{ $title }}</h5>
<a href="{{ route('users.create') }}" class="btn btn-primary mb-4">Tambah Data {{ $title }}</a>
<div class="table-responsive">
<table id="datatable" class="table table-striped">
<thead>
<th>No</th>
<th>Nama</th>
<th>Email</th>
<th>Role</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
@foreach ($users as $item)
<tr>
<td>{{ $loop->iteration }}</td>
<td>{{ $item->name }}</td>
<td>{{ $item->email }}</td>
<td>{{ $item->role }}</td>
<td>
<a href="{{ route('users.edit', $item->id) }}" class="btn btn-warning btn-sm">Ubah</a>
<form id="deleteForm{{ $item->id }}"
action="{{ route('users.destroy', $item->id) }}" method="POST" class="d-inline">
@csrf
@method('DELETE')
<button type="button" class="btn btn-danger btn-sm"
onclick="confirmDelete({{ $item->id }})">Hapus</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
@endsection
@section('js')
<script>
$(document).ready(function() {
$('#datatable').DataTable();
});
// Script untuk SweetAlert
function confirmDelete(id) {
swal({
title: "Apakah anda yakin?",
text: "Data yang dihapus tidak dapat dikembalikan!",
icon: "warning",
buttons: true,
dangerMode: true,
})
.then((willDelete) => {
if (willDelete) {
// Jika pengguna menekan "OK", submit form
$('#deleteForm' + id).submit();
} else {
// Jika pengguna menekan "Cancel"
swal("Data tidak jadi dihapus!", {
icon: "error",
});
}
});
}
</script>
@endsection
@extends('admin.layouts.app')
@section('css')
{{-- CSS Tambahan --}}
@endsection
@section('content')
<div class="card">
<div class="card-body">
<h5 class="card-title fw-semibold mb-4">Data {{ $title }}</h5>
<a href="{{ route('category.create') }}" class="btn btn-primary mb-4">Tambah Data {{ $title }}</a>
<div class="table-responsive">
<table id="datatable" class="table table-striped">
<thead>
<tr>
<th>No</th>
<th>Nama</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
@foreach ($categories as $item)
<tr>
<td>{{ $loop->iteration }}</td>
<td>{{ $item->name }}</td>
<td>
<a href="{{ route('category.edit', $item->id) }}" class="btn btn-warning btn-sm">Ubah</a>
<form id="deleteForm{{ $item->id }}"
action="{{ route('category.destroy', $item->id) }}" method="POST" class="d-inline">
@csrf
@method('DELETE')
<button type="button" class="btn btn-danger btn-sm"
onclick="confirmDelete({{ $item->id }})">Hapus</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
@endsection
@section('js')
<script>
$(document).ready(function() {
$('#datatable').DataTable();
});
function confirmDelete(id) {
swal({
title: "Apakah anda yakin?",
text: "Data yang dihapus tidak dapat dikembalikan!",
icon: "warning",
buttons: true,
dangerMode: true,
})
.then((willDelete) => {
if (willDelete) {
$('#deleteForm' + id).submit();
} else {
swal("Data tidak jadi dihapus!", {
icon: "error",
});
}
});
}
</script>
@endsection
Agar halaman daftar kategori bisa diakses, kita perlu menambahkan link menunya di sidebar. Prosesnya sama persis seperti saat kita menambahkan menu Users.
{{-- (Kode Menu Home & Dashboard) --}}
<li class="nav-small-cap">
<i class="ti ti-dots nav-small-cap-icon fs-4"></i>
<span class="hide-menu">Datamaster</span>
</li>
{{-- Menu Users --}}
<li class="sidebar-item {{ Request::is('users*') ? 'selected' : '' }}">
<a class="sidebar-link {{ Request::is('users*') ? 'active' : '' }}"
href="{{ route('users.index') }}" aria-expanded="false">
<span>
<i class="ti ti-users"></i>
</span>
<span class="hide-menu">Data Users</span>
</a>
</li>
{{-- Ini adalah kode hasil duplikasi yang akan kita ubah --}}
<li class="sidebar-item {{ Request::is('users*') ? 'selected' : '' }}">
<a class="sidebar-link {{ Request::is('users*') ? 'active' : '' }}"
href="{{ route('users.index') }}" aria-expanded="false">
<span>
<i class="ti ti-users"></i>
</span>
<span class="hide-menu">Data Users</span>
</a>
</li>
<ul id="sidebarnav">
{{-- MENU HOME --}}
<li class="nav-small-cap">
<i class="ti ti-dots nav-small-cap-icon fs-4"></i>
<span class="hide-menu">Home</span>
</li>
<li class="sidebar-item {{ Request::is('dashboard') ? 'selected' : '' }}">
<a class="sidebar-link {{ Request::is('dashboard') ? 'active' : '' }}"
href="{{ route('dashboard') }}" aria-expanded="false">
<span>
<i class="ti ti-layout-dashboard"></i>
</span>
<span class="hide-menu">Dashboard</span>
</a>
</li>
{{-- MENU DATAMASTER --}}
<li class="nav-small-cap">
<i class="ti ti-dots nav-small-cap-icon fs-4"></i>
<span class="hide-menu">Datamaster</span>
</li>
{{-- Menu Users --}}
<li class="sidebar-item {{ Request::is('users*') ? 'selected' : '' }}">
<a class="sidebar-link {{ Request::is('users*') ? 'active' : '' }}"
href="{{ route('users.index') }}" aria-expanded="false">
<span>
<i class="ti ti-users"></i>
</span>
<span class="hide-menu">Data Users</span>
</a>
</li>
{{-- Menu Category --}}
<li class="sidebar-item {{ Request::is('category*') ? 'selected' : '' }}">
<a class="sidebar-link {{ Request::is('category*') ? 'active' : '' }}"
href="{{ route('category.index') }}" aria-expanded="false">
<span>
<i class="ti ti-category"></i>
</span>
<span class="hide-menu">Data Category</span>
</a>
</li>
</ul>
Sekarang kita akan membuat fungsionalitas untuk menambah data kategori baru. Kita akan mengadaptasi form tambah user yang sudah ada.
public function create()
{
$data['title'] = $this->title;
$data['menu'] = $this->menu;
return view($this->directory . '.create', $data);
}
@extends('admin.layouts.app')
@section('css')
{{-- CSS Tambahan --}}
@endsection
@section('content')
<div class="card">
<div class="card-body">
<h5 class="card-title fw-semibold mb-4">Tambah Data {{ $title }}</h5>
<div class="card">
<div class="card-body">
<form action="{{ route('users.store') }}" method="POST">
@csrf
{{-- Nama --}}
<div class="mb-3">
<label for="name" class="form-label">Nama</label>
<input type="text" class="form-control @error('name') is-invalid @enderror" name="name"
id="name" placeholder="Nama Lengkap" value="{{ old('name') }}">
@error('name')
<small class="text-danger">{{ $message }}</small>
@enderror
</div>
{{-- Email --}}
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" class="form-control @error('email') is-invalid @enderror" name="email"
id="email" placeholder="contoh@email.com" value="{{ old('email') }}">
@error('email')
<small class="text-danger">{{ $message }}</small>
@enderror
</div>
{{-- Password --}}
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control @error('password') is-invalid @enderror"
name="password" id="password" placeholder="Password">
@error('password')
<small class="text-danger">{{ $message }}</small>
@enderror
</div>
{{-- Role --}}
<div class="mb-3">
<label for="role" class="form-label">Role</label>
<select class="form-select @error('role') is-invalid @enderror" name="role" id="role">
<option value="Siswa" {{ old('role') == 'Siswa' ? 'selected' : '' }}>Siswa</option>
</select>
@error('role')
<small class="text-danger">{{ $message }}</small>
@enderror
</div>
{{-- Tombol --}}
<button type="submit" class="btn btn-primary">Simpan</button>
<a href="{{ route('users.index') }}" class="btn btn-warning">Kembali</a>
</form>
</div>
</div>
</div>
</div>
@endsection
@section('js')
{{-- JS Tambahan --}}
@endsection
@extends('admin.layouts.app')
@section('css')
{{-- CSS Tambahan --}}
@endsection
@section('content')
<div class="card">
<div class="card-body">
<h5 class="card-title fw-semibold mb-4">Tambah Data {{ $title }}</h5>
<div class="card">
<div class="card-body">
<form action="{{ route('category.store') }}" method="POST">
@csrf
{{-- Nama --}}
<div class="mb-3">
<label for="name" class="form-label">Nama Kategori</label>
<input type="text" class="form-control @error('name') is-invalid @enderror" name="name"
id="name" placeholder="Nama Kategori" value="{{ old('name') }}">
@error('name')
<small class="text-danger">{{ $message }}</small>
@enderror
</div>
{{-- Tombol --}}
<button type="submit" class="btn btn-primary">Simpan</button>
<a href="{{ route('category.index') }}" class="btn btn-warning">Kembali</a>
</form>
</div>
</div>
</div>
</div>
@endsection
@section('js')
{{-- JS Tambahan --}}
@endsection
Sekarang kita akan menulis logika di controller untuk memvalidasi dan menyimpan data kategori baru ke database.
public function store(Request $request)
{
// 1. Validasi data
$validatedData = $request->validate([
'name' => 'required|max:255',
'email' => 'required|email|unique:users',
'password' => 'required|min:5',
'role' => 'required'
]);
// 2. Enkripsi password
$validatedData['password'] = Hash::make($validatedData['password']);
// 3. Simpan data ke database
$user = User::create($validatedData);
// 4. Redirect dengan pesan sukses
if ($user) {
return redirect()->route('users.index')->with([
'status' => 'success',
'title' => 'Berhasil',
'message' => 'Data Berhasil Ditambahkan!'
]);
} else {
return redirect()->route('users.index')->with([
'status' => 'danger',
'title' => 'Gagal',
'message' => 'Data Gagal Ditambahkan!'
]);
}
}
public function store(Request $request)
{
// 1. Validasi data
$validatedData = $request->validate([
'name' => 'required|max:255|unique:categories',
]);
// 2. Simpan data ke database
$category = Category::create($validatedData);
// 3. Redirect dengan pesan sukses
if ($category) {
return redirect()->route('category.index')->with([
'status' => 'success',
'title' => 'Berhasil',
'message' => 'Data Berhasil Ditambahkan!'
]);
} else {
return redirect()->route('category.index')->with([
'status' => 'danger',
'title' => 'Gagal',
'message' => 'Data Gagal Ditambahkan!'
]);
}
}
Prosesnya akan sangat mirip dengan membuat form tambah, kita akan mengadaptasi kode yang sudah ada untuk mempercepat pekerjaan.
{
// Menyiapkan data umum
$data['title'] = $this->title;
$data['menu'] = $this->menu;
// Mencari data category berdasarkan ID menggunakan Model Binding
$data['category'] = $category;
// Me-return view beserta data
return view($this->directory . '.edit', $data);
}
@extends('admin.layouts.app')
@section('css')
{{-- CSS Tambahan --}}
@endsection
@section('content')
<div class="card">
<div class="card-body">
<h5 class="card-title fw-semibold mb-4">Ubah Data {{ $title }}</h5> {{-- PERUBAHAN 1 --}}
<div class="card">
<div class="card-body">
<form action="{{ route('category.update', $category->id) }}" method="POST"> {{-- PERUBAHAN 2 --}}
@csrf
@method('PUT') {{-- PERUBAHAN 3 --}}
{{-- Nama --}}
<div class="mb-3">
<label for="name" class="form-label">Nama Kategori</label>
<input type="text" class="form-control @error('name') is-invalid @enderror" name="name"
id="name" placeholder="Nama Kategori" value="{{ old('name', $category->name) }}"> {{-- PERUBAHAN 4 --}}
@error('name')
<small class="text-danger">{{ $message }}</small>
@enderror
</div>
{{-- Tombol --}}
<button type="submit" class="btn btn-primary">Simpan</button>
<a href="{{ route('category.index') }}" class="btn btn-warning">Kembali</a>
</form>
</div>
</div>
</div>
</div>
@endsection
Langkah 10: Menyiapkan Logika Update Data
Saatnya menulis logika di controller untuk memvalidasi dan memperbarui data di database.
public function update(Request $request, Category $category)
{
// 1. Cari data user berdasarkan ID
$user = User::findOrFail($id);
// 2. Validasi data
$validatedData = $request->validate([
'name' => 'required|max:255',
'email' => 'required|email|unique:users,email,' . $id,
'password' => 'nullable|min:5', // Password boleh kosong
'role' => 'required'
]);
// 3. Menyiapkan data untuk diupdate
$updateData = [
'name' => $validatedData['name'],
'email' => $validatedData['email'],
'role' => $validatedData['role'],
];
// 4. Jika password diisi, enkripsi dan tambahkan ke data update
if ($request->filled('password')) {
$updateData['password'] = Hash::make($validatedData['password']);
}
// 5. Update data di database
$updateProcess = $user->update($updateData);
// 6. Redirect dengan pesan sukses
if ($updateProcess) {
return redirect()->route('users.index')->with([
'status' => 'success',
'title' => 'Berhasil',
'message' => 'Data Berhasil Diubah!'
]);
} else {
return redirect()->route('users.index')->with([
'status' => 'danger',
'title' => 'Gagal',
'message' => 'Data Gagal Diubah!'
]);
}
}
public function update(Request $request, Category $category)
{
// 1. Validasi data
$validatedData = $request->validate([
'name' => 'required|max:255|unique:categories,name,' . $category->id,
]);
// 2. Update data di database
$updateProcess = $category->update($validatedData);
// 3. Redirect dengan pesan sukses
if ($updateProcess) {
return redirect()->route('category.index')->with([
'status' => 'success',
'title' => 'Berhasil',
'message' => 'Data Berhasil Diubah!'
]);
} else {
return redirect()->route('category.index')->with([
'status' => 'danger',
'title' => 'Gagal',
'message' => 'Data Gagal Diubah!'
]);
}
}
Langkah 12: Membuat Fitur Hapus Category
Ini adalah langkah terakhir untuk melengkapi CRUD Category. Kita hanya perlu mengisi logika di controller, karena route dan tombol di tampilan sudah kita siapkan sebelumnya.
public function destroy(Category $category)
{
// 1. Cari data user berdasarkan ID
$user = User::findOrFail($id);
// 2. Lakukan proses delete
if ($user) {
$user->delete();
// 3. Jika berhasil, redirect dengan pesan sukses
return redirect()->route('users.index')->with([
'status' => 'success',
'title' => 'Berhasil',
'message' => 'Data Berhasil Dihapus!'
]);
} else {
// 4. Jika gagal, redirect dengan pesan gagal
return redirect()->route('users.index')->with([
'status' => 'danger',
'title' => 'Gagal',
'message' => 'Data Gagal Dihapus!'
]);
}
}
public function destroy(Category $category)
{
$deleteProcess = $category->delete();
if ($deleteProcess) {
return redirect()->route('category.index')->with([
'status' => 'success',
'title' => 'Berhasil',
'message' => 'Data Berhasil Dihapus!'
]);
} else {
return redirect()->route('category.index')->with([
'status' => 'danger',
'title' => 'Gagal',
'message' => 'Data Gagal Dihapus!'
]);
}
}
Penjelasan Kode: Logikanya sangat sederhana. Fungsi ini mencari kategori berdasarkan ID menggunakan Model Binding, menghapusnya dari database menggunakan metode $category->delete(), lalu mengembalikan pengguna ke halaman daftar kategori dengan notifikasi yang sesuai.
Saatnya memastikan semua bagian (Tombol -> SweetAlert -> Controller -> Redirect) bekerja dengan sempurna.
Selamat! Anda telah berhasil membangun fungsionalitas CRUD kedua (Create, Read, Update, Delete) dengan jauh lebih cepat dengan mengadaptasi kode yang sudah ada. Ini adalah keterampilan penting yang menunjukkan pemahaman Anda tentang pola pengembangan di Laravel.
Artikel Lainnya Dengan Kategori Terkait :
1. Inventaris Lab Laravel 12 : Bagian #01 - INSTALASI LARAGON, PHP, PHPMYADMIN & LARAVEL
2. Inventaris Lab Laravel 12 : Bagian #02 - KONFIGURASI DATABASE & FONDASI PROYEK
3. Inventaris Lab Laravel 12 : Bagian #03 - MEMBANGUN HALAMAN ADMIN DENGAN BLADE TEMPLATING
4. Inventaris Lab Laravel 12 : Bagian #04 - MEMBUAT HALAMAN DASHBOARD DINAMIS
5. Inventaris Lab Laravel 12 : Bagian #05 - MEMBUAT HALAMAN CRUD USERS
6. Inventaris Lab Laravel 12 : Bagian #06 - MEMBUAT FITUR AUTENTIKASI (LOGIN) & PENYESUAIAN UI
7. Inventaris Lab Laravel 12 : Bagian #07 - MEMBUAT HALAMAN CRUD CATEGORY
8. Inventaris Lab Laravel 12 : Bagian #08 - MEMBUAT HALAMAN CRUD ITEM
9. Inventaris Lab Laravel 12 : Bagian #09 - MEMBUAT HALAMAN TRANSAKSI PEMINJAMAN (LOAN)
10. Inventaris Lab Laravel 12 : Bagian #10 - HAK AKSES (AUTHORIZATION) & HALAMAN SISWA
Mahardika Oktadiansyah - 15 Juli 2025
Belajar CSS Lanjutan #395 | CSS padding-inline Property
Mahardika Oktadiansyah - 15 Juli 2025
Belajar CSS Lanjutan #394 | CSS padding-bottom Property
Mahardika Oktadiansyah - 15 Juli 2025
Belajar CSS Lanjutan #393 | CSS padding-block-start Property