Day 12 โ CRUD Master Data: Category โ
Fase: 2 โ Mini Project Asset Management | Minggu: 3 | Hari: Selasa
๐ฏ Tujuan Hari Ini โ
Tim menyelesaikan modul CRUD Category secara lengkap: model, controller, route, dan 4 view (index, create, edit, show), dengan validasi data yang rapi.
๐ Task Wajib โ
1. Bagi Tugas Hari Ini โ
| Role | Tugas |
|---|---|
| Backend Dev | Buat Model, Controller, dan logic CRUD Category |
| Frontend Dev | Buat file-file view (HTML + CSS) untuk Category |
| QA Tester | Siapkan test case dan uji fitur saat sudah selesai |
| PM | Koordinasi + jika selesai, bantu setup layout master yang baru |
2. Buat Model Category โ
php artisan make:model CategoryIsi app/Models/Category.php:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
protected $fillable = ['name', 'description'];
// Relasi: 1 kategori punya banyak aset
public function assets()
{
return $this->hasMany(Asset::class);
}
}3. Buat Controller โ
php artisan make:controller CategoryController --resourceIsi method-method di CategoryController.php:
<?php
namespace App\Http\Controllers;
use App\Models\Category;
use Illuminate\Http\Request;
class CategoryController extends Controller
{
public function index()
{
$categories = Category::withCount('assets')->orderBy('name')->get();
return view('categories.index', compact('categories'));
}
public function create()
{
return view('categories.create');
}
public function store(Request $request)
{
$request->validate([
'name' => 'required|min:2|max:100|unique:categories,name',
'description' => 'nullable|max:500',
]);
Category::create($request->only(['name', 'description']));
return redirect()->route('categories.index')
->with('success', "Kategori '{$request->name}' berhasil ditambahkan!");
}
public function show(Category $category)
{
$category->load('assets');
return view('categories.show', compact('category'));
}
public function edit(Category $category)
{
return view('categories.edit', compact('category'));
}
public function update(Request $request, Category $category)
{
$request->validate([
'name' => "required|min:2|max:100|unique:categories,name,{$category->id}",
'description' => 'nullable|max:500',
]);
$category->update($request->only(['name', 'description']));
return redirect()->route('categories.index')
->with('success', "Kategori '{$category->name}' berhasil diperbarui!");
}
public function destroy(Category $category)
{
if ($category->assets()->count() > 0) {
return redirect()->route('categories.index')
->with('error', "Kategori '{$category->name}' tidak bisa dihapus karena masih memiliki aset!");
}
$nama = $category->name;
$category->delete();
return redirect()->route('categories.index')
->with('success', "Kategori '{$nama}' berhasil dihapus!");
}
}Perhatikan logika
destroy(): Kita cek dulu apakah kategori masih punya aset! Ini adalah business rule penting โ tidak boleh hapus kategori yang masih dipakai.
4. Daftarkan Route โ
Di routes/web.php:
use App\Http\Controllers\CategoryController;
Route::resource('categories', CategoryController::class);5. Buat Views (Frontend Dev Mengerjakan Ini) โ
resources/views/categories/index.blade.php:
@extends('layouts.app')
@section('title', 'Kategori Aset')
@section('content')
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:20px;">
<div>
<h1>๐ Kategori Aset</h1>
<p style="color:#666;">Total: {{ $categories->count() }} kategori</p>
</div>
<a href="{{ route('categories.create') }}" class="btn-primary">+ Tambah Kategori</a>
</div>
@include('partials.alert')
<div style="display:grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap:20px;">
@forelse($categories as $cat)
<div style="background:white; border-radius:10px; padding:24px; box-shadow:0 2px 8px rgba(0,0,0,0.07);">
<h3 style="margin-bottom:8px; color:#1a1a2e;">{{ $cat->name }}</h3>
<p style="color:#666; font-size:14px; margin-bottom:16px;">
{{ $cat->description ?? 'Tidak ada deskripsi.' }}
</p>
<div style="display:flex; justify-content:space-between; align-items:center;">
<span style="background:#eef2ff; color:#4f46e5; padding:4px 12px; border-radius:20px; font-size:13px;">
๐๏ธ {{ $cat->assets_count }} aset
</span>
<div style="display:flex; gap:10px;">
<a href="{{ route('categories.edit', $cat->id) }}" style="color:#0066cc; font-size:14px;">Edit</a>
<form action="{{ route('categories.destroy', $cat->id) }}" method="POST"
onsubmit="return confirm('Hapus kategori {{ $cat->name }}?')">
@csrf @method('DELETE')
<button type="submit" style="border:none; background:none; color:#dc3545; cursor:pointer; font-size:14px;">Hapus</button>
</form>
</div>
</div>
</div>
@empty
<p style="color:#999;">Belum ada kategori. <a href="{{ route('categories.create') }}">Tambahkan sekarang.</a></p>
@endforelse
</div>
@endsectionresources/views/categories/create.blade.php dan edit.blade.php ikuti pola Day 7โ8 (form dengan @extends + validasi error).
6. Buat Partial Alert โ
Buat file resources/views/partials/alert.blade.php:
@if(session('success'))
<div style="background:#d4edda; color:#155724; padding:12px 16px; border-radius:6px; margin-bottom:20px;">
โ
{{ session('success') }}
</div>
@endif
@if(session('error'))
<div style="background:#f8d7da; color:#721c24; padding:12px 16px; border-radius:6px; margin-bottom:20px;">
โ {{ session('error') }}
</div>
@endifGunakan di setiap view dengan: @include('partials.alert')
๐ Laporan ke Mentor โ
๐ LAPORAN HARIAN โ Day 12
Nama : [Nama Lengkap]
Role : [Role Kamu]
โ
Yang saya kerjakan hari ini:
- [ ] Model Category dengan relasi hasMany selesai
- [ ] CategoryController dengan semua 7 method selesai
- [ ] Validasi unique nama kategori berjalan
- [ ] Logika "tidak bisa hapus jika ada aset" berjalan
- [ ] View index, create, edit selesai
- [ ] Semua CRUD Category diuji manual
๐ธ Screenshot Wajib:
1. Halaman /categories menampilkan kartu-kartu kategori
2. Form tambah kategori
3. Pesan error saat mencoba hapus kategori yang masih punya aset
๐งช QA Test Result (diisi oleh QA Tester):
| Test Case | Expected | Result | Pass/Fail |
|-----------|----------|--------|-----------|
| Tambah kategori baru | Tersimpan | | |
| Tambah kategori nama duplikat | Validasi error | | |
| Edit nama kategori | Berhasil update | | |
| Hapus kategori tanpa aset | Berhasil hapus | | |
| Hapus kategori dengan aset | Muncul pesan error | | |
โ Kendala:
[Tulis jika ada]Catatan Mentor
Field withCount('assets') di index() adalah cara elegan mendapatkan jumlah relasi tanpa query terpisah. Ini adalah pola yang sangat umum di industri. Jelaskan kepada peserta.