HARI 3: Database dan Eloquent ORM
Bootcamp Laravel - Sistem Pengelolaan Dokumen (SiDoku)
📋 Informasi Sesi
| Item | Keterangan |
|---|---|
| Hari | 3 dari 5 |
| Durasi | 8 Jam (09:00 - 17:00 WIB) |
| Topik Utama | Database, Migration, Eloquent ORM |
| Project | SiDoku - Implementasi database |
🎯 Learning Objectives
Setelah menyelesaikan sesi ini, peserta akan mampu:
- Memahami konsep database dan MySQL
- Membuat dan menjalankan Migration
- Menggunakan Seeder untuk data dummy
- Memahami dan menggunakan Eloquent ORM
- Membuat relasi antar model (One-to-Many, Many-to-Many)
- Melakukan operasi CRUD dengan Eloquent
SESI 1: Pengenalan Database di Laravel
1.1 Konfigurasi Database
Laravel mendukung berbagai database:
- MySQL / MariaDB
- PostgreSQL
- SQLite
- SQL Server
Konfigurasi di .env:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=sidoku
DB_USERNAME=root
DB_PASSWORD=1.2 Membuat Database
Via Laragon HeidiSQL:
- Klik Laragon > Database > HeidiSQL
- Klik kanan > Create new > Database
- Nama:
sidoku - Charset:
utf8mb4 - Collation:
utf8mb4_unicode_ci
Via MySQL CLI:
mysql -u root -p
CREATE DATABASE sidoku CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
SHOW DATABASES;
exit;1.3 Test Koneksi
php artisan db:showatau
php artisan tinker
DB::connection()->getPdo();Jika tidak ada error, koneksi berhasil.
SESI 2: Migration
2.1 Pengenalan Migration
Migration adalah version control untuk database. Dengan migration, Anda dapat:
- Membuat tabel secara programatik
- Berbagi struktur database dengan tim
- Rollback perubahan jika terjadi kesalahan
2.2 Membuat Migration
php artisan make:migration create_kategoris_tableHasil: database/migrations/2025_06_20_000000_create_kategoris_table.php
Edit migration:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('kategoris', function (Blueprint $table) {
$table->id();
$table->string('nama', 100);
$table->string('kode', 20)->unique();
$table->text('deskripsi')->nullable();
$table->boolean('aktif')->default(true);
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('kategoris');
}
};2.3 Tipe Data Column
| Method | Keterangan |
|---|---|
$table->id() | Auto-increment primary key |
$table->string('nama', 100) | VARCHAR(100) |
$table->text('deskripsi') | TEXT |
$table->integer('jumlah') | INTEGER |
$table->bigInteger('total') | BIGINT |
$table->float('nilai') | FLOAT |
$table->decimal('harga', 10, 2) | DECIMAL(10,2) |
$table->boolean('aktif') | BOOLEAN/TINYINT(1) |
$table->date('tanggal') | DATE |
$table->datetime('waktu') | DATETIME |
$table->timestamp('created_at') | TIMESTAMP |
$table->json('data') | JSON |
$table->enum('status', ['draft', 'aktif']) | ENUM |
Modifiers:
| Modifier | Keterangan |
|---|---|
->nullable() | Boleh NULL |
->default('value') | Nilai default |
->unique() | Harus unik |
->unsigned() | Tanpa negatif |
->after('column') | Posisi setelah column |
2.4 Migration untuk Tabel Dokumen
php artisan make:migration create_dokumens_tableEdit:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('dokumens', function (Blueprint $table) {
$table->id();
$table->foreignId('kategori_id')->constrained('kategoris')->onDelete('cascade');
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->string('judul', 255);
$table->string('nomor', 50)->unique();
$table->text('deskripsi')->nullable();
$table->string('file_path')->nullable();
$table->enum('status', ['draft', 'pending', 'approved', 'rejected'])->default('draft');
$table->date('tanggal_dokumen');
$table->timestamps();
$table->softDeletes();
});
}
public function down(): void
{
Schema::dropIfExists('dokumens');
}
};2.5 Menjalankan Migration
# Jalankan semua migration
php artisan migrate
# Lihat status migration
php artisan migrate:status
# Rollback migration terakhir
php artisan migrate:rollback
# Rollback semua dan migrate ulang
php artisan migrate:fresh
# Fresh dengan seeder
php artisan migrate:fresh --seed2.6 Modifikasi Tabel
Menambah column:
php artisan make:migration add_prioritas_to_dokumens_tablepublic function up(): void
{
Schema::table('dokumens', function (Blueprint $table) {
$table->enum('prioritas', ['rendah', 'sedang', 'tinggi'])
->default('sedang')
->after('status');
});
}
public function down(): void
{
Schema::table('dokumens', function (Blueprint $table) {
$table->dropColumn('prioritas');
});
}SESI 3: Seeder dan Factory
3.1 Membuat Seeder
php artisan make:seeder KategoriSeederEdit database/seeders/KategoriSeeder.php:
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class KategoriSeeder extends Seeder
{
public function run(): void
{
$kategoris = [
[
'nama' => 'Surat Keputusan',
'kode' => 'SK',
'deskripsi' => 'Surat keputusan resmi dari pimpinan',
'aktif' => true,
'created_at' => now(),
'updated_at' => now(),
],
[
'nama' => 'Nota Dinas',
'kode' => 'ND',
'deskripsi' => 'Nota dinas internal',
'aktif' => true,
'created_at' => now(),
'updated_at' => now(),
],
[
'nama' => 'Surat Tugas',
'kode' => 'ST',
'deskripsi' => 'Surat penugasan pegawai',
'aktif' => true,
'created_at' => now(),
'updated_at' => now(),
],
[
'nama' => 'Surat Edaran',
'kode' => 'SE',
'deskripsi' => 'Surat edaran untuk informasi umum',
'aktif' => true,
'created_at' => now(),
'updated_at' => now(),
],
];
DB::table('kategoris')->insert($kategoris);
}
}3.2 Membuat Factory
php artisan make:factory DokumenFactoryEdit database/factories/DokumenFactory.php:
<?php
namespace Database\Factories;
use App\Models\Kategori;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
class DokumenFactory extends Factory
{
public function definition(): array
{
$kodeDokumen = ['SK', 'ND', 'ST', 'SE'];
$kode = $this->faker->randomElement($kodeDokumen);
return [
'kategori_id' => Kategori::inRandomOrder()->first()?->id ?? 1,
'user_id' => User::inRandomOrder()->first()?->id ?? 1,
'judul' => $this->faker->sentence(4),
'nomor' => $kode . '/' . $this->faker->unique()->numerify('###') . '/2025',
'deskripsi' => $this->faker->paragraph(),
'status' => $this->faker->randomElement(['draft', 'pending', 'approved', 'rejected']),
'tanggal_dokumen' => $this->faker->dateTimeBetween('-1 year', 'now'),
];
}
// State untuk dokumen approved
public function approved(): static
{
return $this->state(fn (array $attributes) => [
'status' => 'approved',
]);
}
// State untuk dokumen pending
public function pending(): static
{
return $this->state(fn (array $attributes) => [
'status' => 'pending',
]);
}
}3.3 DatabaseSeeder
Edit database/seeders/DatabaseSeeder.php:
<?php
namespace Database\Seeders;
use App\Models\User;
use App\Models\Dokumen;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
public function run(): void
{
// Buat user admin
User::factory()->create([
'name' => 'Admin SiDoku',
'email' => 'admin@sidoku.test',
]);
// Buat 10 user random
User::factory(10)->create();
// Jalankan KategoriSeeder
$this->call([
KategoriSeeder::class,
]);
// Buat 50 dokumen random
Dokumen::factory(50)->create();
// Buat 10 dokumen approved
Dokumen::factory(10)->approved()->create();
}
}3.4 Menjalankan Seeder
# Jalankan DatabaseSeeder
php artisan db:seed
# Jalankan seeder tertentu
php artisan db:seed --class=KategoriSeeder
# Fresh migrate + seed
php artisan migrate:fresh --seedSESI 4: Eloquent ORM
4.1 Pengenalan Eloquent
Eloquent adalah Object-Relational Mapping (ORM) bawaan Laravel yang memudahkan interaksi dengan database menggunakan model PHP.
Konvensi Penamaan:
| Model | Tabel Database |
|---|---|
| User | users |
| Dokumen | dokumens |
| Kategori | kategoris |
| DocumentCategory | document_categories |
4.2 Membuat Model
# Model saja
php artisan make:model Kategori
# Model + Migration
php artisan make:model Kategori -m
# Model + Migration + Factory + Seeder + Controller
php artisan make:model Dokumen -mfsc4.3 Model Kategori
app/Models/Kategori.php:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Kategori extends Model
{
use HasFactory;
// Nama tabel (jika tidak mengikuti konvensi)
protected $table = 'kategoris';
// Field yang bisa diisi mass assignment
protected $fillable = [
'nama',
'kode',
'deskripsi',
'aktif',
];
// Cast tipe data
protected $casts = [
'aktif' => 'boolean',
];
// Relasi: Kategori memiliki banyak Dokumen
public function dokumens(): HasMany
{
return $this->hasMany(Dokumen::class);
}
// Scope: Hanya kategori aktif
public function scopeAktif($query)
{
return $query->where('aktif', true);
}
}4.4 Model Dokumen
app/Models/Dokumen.php:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
class Dokumen extends Model
{
use HasFactory, SoftDeletes;
protected $table = 'dokumens';
protected $fillable = [
'kategori_id',
'user_id',
'judul',
'nomor',
'deskripsi',
'file_path',
'status',
'prioritas',
'tanggal_dokumen',
];
protected $casts = [
'tanggal_dokumen' => 'date',
];
// Relasi: Dokumen milik satu Kategori
public function kategori(): BelongsTo
{
return $this->belongsTo(Kategori::class);
}
// Relasi: Dokumen milik satu User
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
// Accessor: Format tanggal Indonesia
public function getTanggalIndonesiaAttribute(): string
{
return $this->tanggal_dokumen->translatedFormat('d F Y');
}
// Scope: Filter by status
public function scopeStatus($query, $status)
{
return $query->where('status', $status);
}
// Scope: Pencarian
public function scopeCari($query, $keyword)
{
return $query->where(function ($q) use ($keyword) {
$q->where('judul', 'like', "%{$keyword}%")
->orWhere('nomor', 'like', "%{$keyword}%");
});
}
}4.5 Operasi CRUD dengan Eloquent
Create (Insert)
// Cara 1: create() dengan mass assignment
$kategori = Kategori::create([
'nama' => 'Surat Masuk',
'kode' => 'SM',
'deskripsi' => 'Surat masuk dari eksternal',
]);
// Cara 2: new + save
$dokumen = new Dokumen();
$dokumen->judul = 'SK Mutasi Pegawai';
$dokumen->nomor = 'SK/001/2025';
$dokumen->kategori_id = 1;
$dokumen->user_id = 1;
$dokumen->tanggal_dokumen = now();
$dokumen->save();
// Cara 3: firstOrCreate
$kategori = Kategori::firstOrCreate(
['kode' => 'SK'],
['nama' => 'Surat Keputusan', 'deskripsi' => 'SK']
);Read (Select)
// Ambil semua data
$dokumens = Dokumen::all();
// Ambil dengan kondisi
$pending = Dokumen::where('status', 'pending')->get();
// Ambil satu data
$dokumen = Dokumen::find(1);
$dokumen = Dokumen::findOrFail(1); // Throw 404 jika tidak ada
// First
$first = Dokumen::where('status', 'approved')->first();
// Select kolom tertentu
$dokumens = Dokumen::select('id', 'judul', 'nomor')->get();
// Ordering
$dokumens = Dokumen::orderBy('created_at', 'desc')->get();
// Limit
$dokumens = Dokumen::take(10)->get();
// Pagination
$dokumens = Dokumen::paginate(15);
// Dengan relasi (Eager Loading)
$dokumens = Dokumen::with(['kategori', 'user'])->get();Update
// Cara 1: find + save
$dokumen = Dokumen::find(1);
$dokumen->status = 'approved';
$dokumen->save();
// Cara 2: update langsung
Dokumen::where('id', 1)->update(['status' => 'approved']);
// Cara 3: updateOrCreate
Dokumen::updateOrCreate(
['nomor' => 'SK/001/2025'],
['judul' => 'SK Mutasi Updated', 'status' => 'pending']
);Delete
// Cara 1: find + delete
$dokumen = Dokumen::find(1);
$dokumen->delete();
// Cara 2: destroy
Dokumen::destroy(1);
Dokumen::destroy([1, 2, 3]);
// Cara 3: delete dengan kondisi
Dokumen::where('status', 'draft')->delete();
// Soft Delete (jika menggunakan SoftDeletes)
$dokumen->delete(); // Set deleted_at
// Restore soft deleted
$dokumen = Dokumen::withTrashed()->find(1);
$dokumen->restore();
// Force delete (permanent)
$dokumen->forceDelete();4.6 Query Builder Lanjutan
// Multiple conditions
$dokumens = Dokumen::where('status', 'approved')
->where('kategori_id', 1)
->orderBy('tanggal_dokumen', 'desc')
->get();
// Or condition
$dokumens = Dokumen::where('status', 'approved')
->orWhere('status', 'pending')
->get();
// Where In
$dokumens = Dokumen::whereIn('status', ['approved', 'pending'])->get();
// Where Between
$dokumens = Dokumen::whereBetween('tanggal_dokumen', ['2025-01-01', '2025-06-30'])->get();
// Where Null
$dokumens = Dokumen::whereNull('file_path')->get();
// Count
$total = Dokumen::where('status', 'approved')->count();
// Sum, Avg, Max, Min
$stats = Dokumen::selectRaw('
COUNT(*) as total,
SUM(CASE WHEN status = "approved" THEN 1 ELSE 0 END) as approved
')->first();
// Group By
$byKategori = Dokumen::selectRaw('kategori_id, COUNT(*) as total')
->groupBy('kategori_id')
->get();SESI 5: Relasi Eloquent
5.1 Jenis Relasi
| Relasi | Contoh |
|---|---|
| One to One | User hasOne Profile |
| One to Many | Kategori hasMany Dokumen |
| Many to One | Dokumen belongsTo Kategori |
| Many to Many | Dokumen belongsToMany Tag |
5.2 One to Many
Kategori memiliki banyak Dokumen:
// Di Model Kategori
public function dokumens(): HasMany
{
return $this->hasMany(Dokumen::class);
}
// Penggunaan
$kategori = Kategori::find(1);
$dokumens = $kategori->dokumens; // Collection of DokumenDokumen milik satu Kategori:
// Di Model Dokumen
public function kategori(): BelongsTo
{
return $this->belongsTo(Kategori::class);
}
// Penggunaan
$dokumen = Dokumen::find(1);
$namaKategori = $dokumen->kategori->nama;5.3 Many to Many
Contoh: Dokumen dan Tag
Migration untuk pivot table:
php artisan make:migration create_dokumen_tag_tablepublic function up(): void
{
Schema::create('dokumen_tag', function (Blueprint $table) {
$table->id();
$table->foreignId('dokumen_id')->constrained('dokumens')->onDelete('cascade');
$table->foreignId('tag_id')->constrained()->onDelete('cascade');
$table->timestamps();
});
}Model Tag:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
class Tag extends Model
{
protected $fillable = ['nama', 'slug'];
public function dokumens(): BelongsToMany
{
return $this->belongsToMany(Dokumen::class, 'dokumen_tag');
}
}Di Model Dokumen:
public function tags(): BelongsToMany
{
return $this->belongsToMany(Tag::class, 'dokumen_tag');
}Operasi Many to Many:
// Attach tags ke dokumen
$dokumen = Dokumen::find(1);
$dokumen->tags()->attach([1, 2, 3]);
// Detach
$dokumen->tags()->detach([1, 2]);
// Sync (replace all)
$dokumen->tags()->sync([1, 3, 5]);
// Toggle
$dokumen->tags()->toggle([1, 2]);
// Ambil dokumen dengan tags
$dokumen = Dokumen::with('tags')->find(1);
foreach ($dokumen->tags as $tag) {
echo $tag->nama;
}5.4 Eager Loading
Menghindari N+1 query problem:
// [X] N+1 Problem (banyak query)
$dokumens = Dokumen::all();
foreach ($dokumens as $dok) {
echo $dok->kategori->nama; // Query untuk setiap dokumen
}
// [OK] Eager Loading (hanya 2 query)
$dokumens = Dokumen::with('kategori')->get();
foreach ($dokumens as $dok) {
echo $dok->kategori->nama; // Tidak ada query tambahan
}
// Multiple relations
$dokumens = Dokumen::with(['kategori', 'user', 'tags'])->get();
// Nested relations
$dokumens = Dokumen::with('kategori.dokumens')->get();
// Conditional eager loading
$dokumens = Dokumen::with(['kategori' => function ($query) {
$query->where('aktif', true);
}])->get();PRAKTIKUM: Implementasi Database SiDoku
Langkah 1: Buat Semua Migration
php artisan make:migration create_kategoris_table
php artisan make:migration create_dokumens_table
php artisan make:migration create_tags_table
php artisan make:migration create_dokumen_tag_tableLangkah 2: Buat Model
php artisan make:model Kategori -f
php artisan make:model Dokumen -f
php artisan make:model Tag -fLangkah 3: Jalankan Migration dan Seeder
php artisan migrate:fresh --seedLangkah 4: Update Controller
app/Http/Controllers/DokumenController.php:
<?php
namespace App\Http\Controllers;
use App\Models\Dokumen;
use App\Models\Kategori;
use Illuminate\Http\Request;
class DokumenController extends Controller
{
public function index(Request $request)
{
$query = Dokumen::with(['kategori', 'user']);
// Filter by status
if ($request->has('status')) {
$query->where('status', $request->status);
}
// Search
if ($request->has('search')) {
$query->cari($request->search);
}
$dokumens = $query->orderBy('created_at', 'desc')->paginate(10);
return view('dokumen.index', compact('dokumens'));
}
public function create()
{
$kategoris = Kategori::aktif()->get();
return view('dokumen.create', compact('kategoris'));
}
public function store(Request $request)
{
$validated = $request->validate([
'judul' => 'required|string|max:255',
'nomor' => 'required|string|max:50|unique:dokumens',
'kategori_id' => 'required|exists:kategoris,id',
'deskripsi' => 'nullable|string',
'tanggal_dokumen' => 'required|date',
]);
$validated['user_id'] = auth()->id() ?? 1;
$validated['status'] = 'draft';
Dokumen::create($validated);
return redirect()->route('dokumen.index')
->with('success', 'Dokumen berhasil ditambahkan!');
}
public function show(Dokumen $dokumen)
{
$dokumen->load(['kategori', 'user', 'tags']);
return view('dokumen.show', compact('dokumen'));
}
public function edit(Dokumen $dokumen)
{
$kategoris = Kategori::aktif()->get();
return view('dokumen.edit', compact('dokumen', 'kategoris'));
}
public function update(Request $request, Dokumen $dokumen)
{
$validated = $request->validate([
'judul' => 'required|string|max:255',
'nomor' => 'required|string|max:50|unique:dokumens,nomor,' . $dokumen->id,
'kategori_id' => 'required|exists:kategoris,id',
'deskripsi' => 'nullable|string',
'tanggal_dokumen' => 'required|date',
]);
$dokumen->update($validated);
return redirect()->route('dokumen.index')
->with('success', 'Dokumen berhasil diupdate!');
}
public function destroy(Dokumen $dokumen)
{
$dokumen->delete();
return redirect()->route('dokumen.index')
->with('success', 'Dokumen berhasil dihapus!');
}
}Langkah 5: Update View
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>
{{-- Filter dan Search --}}
<form method="GET" class="mb-6 flex gap-4">
<input type="text"
name="search"
value="{{ request('search') }}"
placeholder="Cari judul atau nomor..."
class="border rounded px-3 py-2 w-64">
<select name="status" class="border rounded px-3 py-2">
<option value="">Semua Status</option>
<option value="draft" {{ request('status') == 'draft' ? 'selected' : '' }}>Draft</option>
<option value="pending" {{ request('status') == 'pending' ? 'selected' : '' }}>Pending</option>
<option value="approved" {{ request('status') == 'approved' ? 'selected' : '' }}>Approved</option>
<option value="rejected" {{ request('status') == 'rejected' ? 'selected' : '' }}>Rejected</option>
</select>
<button type="submit" class="bg-gray-500 text-white px-4 py-2 rounded">Filter</button>
</form>
{{-- Flash message --}}
@if(session('success'))
<x-alert type="success" :message="session('success')" />
@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">Nomor</th>
<th class="border p-2 text-left">Judul</th>
<th class="border p-2 text-left">Kategori</th>
<th class="border p-2 text-left">Status</th>
<th class="border p-2 text-left">Tanggal</th>
<th class="border p-2 text-left">Aksi</th>
</tr>
</thead>
<tbody>
@forelse($dokumens as $dok)
<tr class="hover:bg-gray-50">
<td class="border p-2">{{ $loop->iteration + ($dokumens->currentPage() - 1) * $dokumens->perPage() }}</td>
<td class="border p-2">{{ $dok->nomor }}</td>
<td class="border p-2">{{ $dok->judul }}</td>
<td class="border p-2">{{ $dok->kategori->nama ?? '-' }}</td>
<td class="border p-2">
<span class="px-2 py-1 rounded text-xs
@if($dok->status == 'approved') bg-green-100 text-green-800
@elseif($dok->status == 'pending') bg-yellow-100 text-yellow-800
@elseif($dok->status == 'rejected') bg-red-100 text-red-800
@else bg-gray-100 text-gray-800
@endif">
{{ ucfirst($dok->status) }}
</span>
</td>
<td class="border p-2">{{ $dok->tanggal_dokumen->format('d/m/Y') }}</td>
<td class="border p-2">
<a href="{{ route('dokumen.show', $dok) }}" class="text-blue-500 hover:underline">Lihat</a>
<a href="{{ route('dokumen.edit', $dok) }}" class="text-yellow-500 hover:underline ml-2">Edit</a>
<form action="{{ route('dokumen.destroy', $dok) }}" method="POST" class="inline">
@csrf
@method('DELETE')
<button type="submit"
class="text-red-500 hover:underline ml-2"
onclick="return confirm('Yakin hapus?')">Hapus</button>
</form>
</td>
</tr>
@empty
<tr>
<td colspan="7" class="border p-4 text-center text-gray-500">
Belum ada dokumen
</td>
</tr>
@endforelse
</tbody>
</table>
{{-- Pagination --}}
<div class="mt-4">
{{ $dokumens->withQueryString()->links() }}
</div>
</div>
@endsection✅ Checkpoint Hari 3
Pastikan Anda sudah bisa:
- Membuat dan menjalankan migration
- Menggunakan seeder dan factory
- Membuat model dengan fillable, casts, dan relasi
- Melakukan operasi CRUD dengan Eloquent
- Menggunakan relasi One-to-Many dan Many-to-Many
- Menggunakan eager loading untuk optimasi query
Selamat! Anda siap untuk Hari 4: Advanced Features dan API!