đŸ’ģ Pemrograman Web 2
🎓 Pertemuan
Pertemuan 10: Database Dengan Migration & Model

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: 10 dari 16
Durasi: 150 menit (3 × 50 menit)
Studi Kasus Berkelanjutan: Sistem Manajemen Perpustakaan


PERTEMUAN 10

DATABASE DENGAN MIGRATION & MODEL

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.2: Mampu menjelaskan struktur folder dan alur kerja framework Laravel.
Indikator PencapaianMahasiswa mampu:
1. Menjelaskan konsep database migration di Laravel
2. Membuat migration untuk tabel database
3. Menjalankan dan rollback migration
4. Memahami konsep Eloquent ORM
5. Membuat Model dan relasi dengan database
6. Menggunakan database seeding untuk data awal
7. Menerapkan migration & model dalam sistem perpustakaan
Alokasi Waktuâ€ĸ Teori: 60 menit
â€ĸ Praktikum: 90 menit
â€ĸ Total: 150 menit (3 × 50 menit)

B. PENDAHULUAN

1. Deskripsi Singkat

Pertemuan kesepuluh ini membahas pengelolaan database menggunakan Laravel Migration dan Eloquent ORM. Migration memungkinkan version control untuk struktur database, sedangkan Eloquent menyediakan cara elegan untuk berinteraksi dengan database menggunakan konsep OOP. Mahasiswa akan mempelajari cara membuat, menjalankan, dan memanipulasi struktur database melalui migration, serta membuat Model untuk representasi tabel database.

2. Keterkaitan dengan Pertemuan Lain

Pertemuan ini merupakan fondasi penting untuk pengembangan aplikasi Laravel:

  • Pertemuan 6: Melanjutkan konsep database MySQL yang telah dipelajari
  • Pertemuan 7: Migration menggantikan cara manual membuat tabel dengan SQL
  • Pertemuan 9: Menggunakan Laravel project yang telah dibuat
  • Pertemuan 11-13: Model akan digunakan intensif untuk CRUD operations
  • Pertemuan 14-15: Relasi antar model untuk fitur transaksi

3. Manfaat Pembelajaran

  1. Mampu mengelola struktur database dengan version control
  2. Dapat bekerja tim dengan migration yang konsisten
  3. Memahami ORM untuk interaksi database yang lebih mudah
  4. Siap mengimplementasikan CRUD dengan Eloquent
  5. Menguasai best practice Laravel database management

4. Relevansi dengan Studi Kasus

Dalam sistem perpustakaan, migration & model digunakan untuk:

  • Membuat struktur tabel buku, anggota, transaksi
  • Version control perubahan struktur database
  • Eloquent Model untuk operasi CRUD
  • Seeding data sample untuk testing
  • Relasi antar tabel (buku-transaksi, anggota-transaksi)

C. MATERI TEORI

1. Konsep Database Migration

a. Apa itu Migration?

Migration adalah sistem version control untuk database schema. Migration memungkinkan tim developer untuk:

  • Mendefinisikan dan membagikan struktur database aplikasi
  • Memodifikasi database schema dengan mudah
  • Rollback perubahan jika terjadi masalah
  • Tracking perubahan struktur database

Analogi: Migration seperti Git untuk database - setiap perubahan tercatat dan bisa di-undo.

b. Mengapa Menggunakan Migration?

Tanpa Migration (Cara Lama):

-- Developer A membuat tabel manual
CREATE TABLE buku (
    id INT PRIMARY KEY AUTO_INCREMENT,
    judul VARCHAR(200)
);
 
-- Developer B tidak tahu perubahan ini
-- Database tidak sync
-- Deployment ke production risky

Dengan Migration:

// File migration yang bisa di-commit ke Git
Schema::create('buku', function (Blueprint $table) {
    $table->id();
    $table->string('judul', 200);
    $table->timestamps();
});
 
// Semua developer jalankan: php artisan migrate
// Database otomatis sync

Keuntungan Migration:

  • ✅ Version control untuk database
  • ✅ Mudah rollback jika error
  • ✅ Konsisten di semua environment (dev, staging, production)
  • ✅ Dokumentasi otomatis perubahan database
  • ✅ Kolaborasi tim lebih mudah

c. Struktur Migration File

Migration file Laravel memiliki 2 method utama:

<?php
 
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
 
return new class extends Migration
{
    /**
     * Run the migrations.
     * Method ini dijalankan saat: php artisan migrate
     */
    public function up(): void
    {
        Schema::create('buku', function (Blueprint $table) {
            // Definisi struktur tabel
        });
    }
 
    /**
     * Reverse the migrations.
     * Method ini dijalankan saat: php artisan migrate:rollback
     */
    public function down(): void
    {
        Schema::dropIfExists('buku');
    }
};

Penjelasan:

  • up(): Jalankan perubahan (create, modify table)
  • down(): Batalkan perubahan (drop, revert table)

d. Migration Workflow

1. Developer buat migration file
   ↓
2. Definisikan schema di method up()
   ↓
3. Commit file migration ke Git
   ↓
4. Team pull dari Git
   ↓
5. Jalankan: php artisan migrate
   ↓
6. Database semua orang sync

2. Laravel Schema Builder

a. Tipe Data Column

Laravel menyediakan banyak method untuk mendefinisikan column:

MethodTipe Data SQLKeterangan
$table->id()BIGINT UNSIGNED (PK)Primary key auto increment
$table->string('nama')VARCHAR(255)String default 255 char
$table->string('nama', 100)VARCHAR(100)String custom length
$table->text('deskripsi')TEXTText panjang
$table->integer('stok')INTInteger
$table->bigInteger('jumlah')BIGINTInteger besar
$table->decimal('harga', 10, 2)DECIMAL(10,2)Decimal (harga)
$table->boolean('aktif')BOOLEANTrue/False
$table->date('tanggal')DATEDate only
$table->dateTime('waktu')DATETIMEDate + time
$table->timestamps()created_at, updated_atAuto timestamp
$table->softDeletes()deleted_atSoft delete

Contoh Penggunaan:

Schema::create('buku', function (Blueprint $table) {
    $table->id();                              // Primary key
    $table->string('kode_buku', 20)->unique(); // Unique constraint
    $table->string('judul', 200);              // Required
    $table->string('isbn', 20)->nullable();    // Optional
    $table->text('deskripsi')->nullable();     // Text optional
    $table->integer('stok')->default(0);       // Default value
    $table->decimal('harga', 10, 2);           // Price
    $table->year('tahun_terbit');              // Year
    $table->timestamps();                       // created_at, updated_at
});

b. Column Modifiers

Modifier untuk mengubah behavior column:

$table->string('email')->nullable();        // Boleh NULL
$table->string('kode')->unique();           // Harus unique
$table->integer('stok')->default(0);        // Default value
$table->string('status')->default('aktif'); // Default string
$table->string('judul')->comment('Judul buku'); // Comment
$table->integer('urutan')->unsigned();      // Tidak boleh negatif
$table->text('deskripsi')->nullable()->default(null);

c. Index & Constraint

// Primary Key (otomatis dengan id())
$table->id();
 
// Unique
$table->string('email')->unique();
// atau
$table->unique('email');
 
// Index (untuk performa query)
$table->index('kode_buku');
$table->index(['kategori', 'tahun']); // Composite index
 
// Foreign Key (akan dipelajari detail di pertemuan 14)
$table->foreignId('kategori_id')
      ->constrained('kategoris')
      ->onDelete('cascade');

d. Memodifikasi Table

Menambah Column:

Schema::table('buku', function (Blueprint $table) {
    $table->string('penerbit', 100)->after('pengarang');
    $table->string('bahasa', 20)->default('Indonesia');
});

Mengubah Column:

Schema::table('buku', function (Blueprint $table) {
    $table->string('judul', 300)->change(); // Ubah length
    $table->integer('stok')->unsigned()->change(); // Tambah unsigned
});

Menghapus Column:

Schema::table('buku', function (Blueprint $table) {
    $table->dropColumn('isbn');
    $table->dropColumn(['isbn', 'bahasa']); // Multiple
});

Rename Column:

Schema::table('buku', function (Blueprint $table) {
    $table->renameColumn('judul', 'title');
});

3. Eloquent ORM (Object-Relational Mapping)

a. Apa itu Eloquent?

Eloquent adalah ORM (Object-Relational Mapping) Laravel yang memungkinkan interaksi dengan database menggunakan object-oriented syntax.

Tanpa ORM (Raw SQL):

$hasil = DB::select('SELECT * FROM buku WHERE kategori = ?', ['Programming']);
foreach ($hasil as $row) {
    echo $row->judul;
}

Dengan Eloquent:

$buku = Buku::where('kategori', 'Programming')->get();
foreach ($buku as $item) {
    echo $item->judul;
}

Keuntungan Eloquent:

  • ✅ Syntax lebih mudah dan readable
  • ✅ Type hinting dan auto-completion
  • ✅ Relasi antar tabel lebih mudah
  • ✅ Built-in validation
  • ✅ Events & observers
  • ✅ Soft deletes
  • ✅ Mass assignment protection

b. Struktur Model

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
 
class Buku extends Model
{
    use HasFactory;
 
    // Nama tabel (opsional jika mengikuti convention)
    protected $table = 'buku';
 
    // Primary key (opsional jika 'id')
    protected $primaryKey = 'id';
 
    // Tipe primary key
    protected $keyType = 'int';
 
    // Auto increment
    public $incrementing = true;
 
    // Timestamps (created_at, updated_at)
    public $timestamps = true;
 
    // Fillable (mass assignment)
    protected $fillable = [
        'kode_buku',
        'judul',
        'kategori',
        'pengarang',
        'penerbit',
        'tahun_terbit',
        'isbn',
        'harga',
        'stok',
    ];
 
    // Guarded (kebalikan fillable)
    // protected $guarded = ['id'];
 
    // Hidden (tidak ditampilkan di JSON)
    protected $hidden = [];
 
    // Casts (konversi tipe data)
    protected $casts = [
        'tahun_terbit' => 'integer',
        'harga' => 'decimal:2',
        'stok' => 'integer',
    ];
}

c. Naming Convention

Laravel menggunakan convention over configuration:

ItemConventionContoh
Model NameSingular, PascalCaseBuku, Anggota
Table NamePlural, snake_casebukus, anggotas
Primary KeyidAuto
Foreign Keymodel_idbuku_id, anggota_id
Timestampscreated_at, updated_atAuto

Mengikuti Convention:

// Model: Buku
// Table: bukus (otomatis plural)
class Buku extends Model {}

Custom Table Name:

// Jika table name tidak mengikuti convention
class Buku extends Model {
    protected $table = 'buku'; // Tanpa 's'
}

d. Mass Assignment

Mass assignment memungkinkan insert/update banyak field sekaligus.

Fillable (Whitelist - Recommended):

class Buku extends Model {
    protected $fillable = [
        'judul',
        'pengarang',
        'harga',
        'stok'
    ];
}
 
// Usage
Buku::create([
    'judul' => 'Laravel 12',
    'pengarang' => 'John Doe',
    'harga' => 100000,
    'stok' => 10
]);

Guarded (Blacklist):

class Buku extends Model {
    protected $guarded = ['id', 'created_at', 'updated_at'];
    // Semua field boleh kecuali yang di-guarded
}

âš ī¸ Security Note: Selalu gunakan $fillable atau $guarded untuk mencegah mass assignment vulnerability.


4. Database Seeding

a. Apa itu Seeding?

Seeding adalah proses mengisi database dengan data sample/dummy untuk testing.

Kegunaan:

  • Development: Data dummy untuk testing
  • Testing: Data konsisten untuk unit test
  • Demo: Data sample untuk presentasi
  • Production: Data initial (kategori, roles, dll)

b. Struktur Seeder

<?php
 
namespace Database\Seeders;
 
use Illuminate\Database\Seeder;
use App\Models\Buku;
 
class BukuSeeder extends Seeder
{
    public function run(): void
    {
        // Insert satu record
        Buku::create([
            'kode_buku' => 'BK-001',
            'judul' => 'Laravel 12 Fundamentals',
            'kategori' => 'Programming',
            'pengarang' => 'John Doe',
            'penerbit' => 'Tech Publisher',
            'tahun_terbit' => 2024,
            'harga' => 150000,
            'stok' => 20
        ]);
 
        // Insert multiple records
        $bukuList = [
            [
                'kode_buku' => 'BK-002',
                'judul' => 'MySQL Advanced',
                'kategori' => 'Database',
                // ...
            ],
            // ... more data
        ];
 
        foreach ($bukuList as $buku) {
            Buku::create($buku);
        }
    }
}

c. Model Factories

Factory untuk generate data dummy secara otomatis:

<?php
 
namespace Database\Factories;
 
use Illuminate\Database\Eloquent\Factories\Factory;
 
class BukuFactory extends Factory
{
    public function definition(): array
    {
        return [
            'kode_buku' => 'BK-' . $this->faker->unique()->numberBetween(1000, 9999),
            'judul' => $this->faker->sentence(3),
            'kategori' => $this->faker->randomElement(['Programming', 'Database', 'Web Design']),
            'pengarang' => $this->faker->name(),
            'penerbit' => $this->faker->company(),
            'tahun_terbit' => $this->faker->year(),
            'harga' => $this->faker->numberBetween(50000, 200000),
            'stok' => $this->faker->numberBetween(0, 50),
        ];
    }
}

Usage:

// Generate 50 buku dummy
Buku::factory()->count(50)->create();

5. Artisan Commands untuk Migration

a. Migration Commands

# Membuat migration baru
php artisan make:migration create_buku_table
php artisan make:migration create_anggota_table
 
# Membuat migration dengan model sekaligus
php artisan make:model Buku -m
php artisan make:model Anggota -m
 
# Menjalankan migration
php artisan migrate
 
# Rollback migration terakhir
php artisan migrate:rollback
 
# Rollback semua migration
php artisan migrate:reset
 
# Rollback dan re-run semua migration
php artisan migrate:refresh
 
# Rollback dan re-run dengan seeding
php artisan migrate:refresh --seed
 
# Drop semua table dan re-migrate
php artisan migrate:fresh
 
# Drop, re-migrate, dan seed
php artisan migrate:fresh --seed
 
# Lihat status migration
php artisan migrate:status

b. Model Commands

# Membuat model
php artisan make:model Buku
 
# Membuat model + migration
php artisan make:model Buku -m
 
# Membuat model + migration + seeder
php artisan make:model Buku -ms
 
# Membuat model + migration + factory + seeder
php artisan make:model Buku -mfs
 
# Membuat model + controller + migration
php artisan make:model Buku -mc
 
# All in one (model + migration + factory + seeder + controller)
php artisan make:model Buku -a

c. Seeder Commands

# Membuat seeder
php artisan make:seeder BukuSeeder
 
# Menjalankan seeder
php artisan db:seed
 
# Menjalankan seeder tertentu
php artisan db:seed --class=BukuSeeder
 
# Fresh migrate + seed
php artisan migrate:fresh --seed

6. Environment Configuration

a. Database Configuration

File: .env

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=perpustakaan_laravel
DB_USERNAME=root
DB_PASSWORD=

Penjelasan:

  • DB_CONNECTION: Driver database (mysql, pgsql, sqlite, sqlsrv)
  • DB_HOST: Host database server
  • DB_PORT: Port database (MySQL default: 3306)
  • DB_DATABASE: Nama database
  • DB_USERNAME: Username database
  • DB_PASSWORD: Password database

b. Membuat Database

Sebelum migrate, buat database terlebih dahulu:

Via phpMyAdmin:

  1. Buka http://localhost/phpmyadmin (opens in a new tab)
  2. Klik "New"
  3. Database name: perpustakaan_laravel
  4. Collation: utf8mb4_unicode_ci
  5. Create

Via Command Line:

mysql -u root -p
CREATE DATABASE perpustakaan_laravel CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
exit;

Via Laravel:

// Tidak recommended, tapi bisa
DB::statement('CREATE DATABASE IF NOT EXISTS perpustakaan_laravel');

D. PRAKTIKUM

1. Tujuan Praktikum

  1. Konfigurasi database Laravel
  2. Membuat migration untuk tabel buku dan anggota
  3. Menjalankan migration
  4. Membuat Model Buku dan Anggota
  5. Membuat seeder untuk data sample
  6. Memahami rollback migration
  7. Testing CRUD sederhana dengan Eloquent

2. PRAKTIKUM 1: Konfigurasi Database Laravel

Tujuan

Setup dan konfigurasi koneksi database Laravel.

Langkah-langkah

a. Buat Database

  1. Buka phpMyAdmin: http://localhost/phpmyadmin
  2. Klik tab "Databases"
  3. Database name: perpustakaan_laravel
  4. Collation: utf8mb4_unicode_ci
  5. Klik "Create"

b. Konfigurasi .env

Edit file .env di root project Laravel:

APP_NAME="Perpustakaan Laravel"
APP_ENV=local
APP_KEY=base64:xxxxx (biarkan apa adanya)
APP_DEBUG=true
APP_URL=http://localhost:8000

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=perpustakaan_laravel
DB_USERNAME=root
DB_PASSWORD=

c. Testing Koneksi

Buat route testing di routes/web.php:

<?php
 
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\DB;
 
Route::get('/', function () {
    return view('welcome');
});
 
// Route test koneksi database
Route::get('/test-db', function () {
    try {
        DB::connection()->getPdo();
        $dbName = DB::connection()->getDatabaseName();
        
        return "Koneksi database berhasil!<br />Database: <strong>{$dbName}</strong>";
    } catch (\Exception $e) {
        return "Koneksi database gagal!<br />Error: " . $e->getMessage();
    }
});

d. Testing

  1. Pastikan server Laravel jalan: php artisan serve
  2. Akses: http://localhost:8000/test-db
  3. Jika berhasil, akan muncul: "Koneksi database berhasil! Database: perpustakaan_laravel"

Troubleshooting:

ErrorSolusi
SQLSTATE[HY000] [1049] Unknown databaseDatabase belum dibuat di phpMyAdmin
SQLSTATE[HY000] [2002] Connection refusedMySQL belum running (start XAMPP)
Access denied for user 'root'@'localhost'Username/password salah di .env

3. PRAKTIKUM 2: Migration Tabel Buku

Tujuan

Membuat migration untuk tabel buku perpustakaan.

Langkah-langkah

a. Buat Migration File

php artisan make:migration create_buku_table

Output:

Created Migration: 2024_02_10_123456_create_buku_table.php

b. Edit Migration File

File: database/migrations/2024_02_10_123456_create_buku_table.php

<?php
 
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
 
return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('buku', function (Blueprint $table) {
            $table->id();
            $table->string('kode_buku', 20)->unique();
            $table->string('judul', 200);
            $table->enum('kategori', [
                'Programming', 
                'Database', 
                'Web Design', 
                'Networking',
                'Data Science'
            ]);
            $table->string('pengarang', 100);
            $table->string('penerbit', 100);
            $table->year('tahun_terbit');
            $table->string('isbn', 20)->nullable();
            $table->decimal('harga', 10, 2);
            $table->integer('stok')->default(0);
            $table->text('deskripsi')->nullable();
            $table->string('bahasa', 20)->default('Indonesia');
            $table->timestamps();
        });
    }
 
    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('buku');
    }
};

c. Jalankan Migration

php artisan migrate

Output:

Migrating: 2024_02_10_123456_create_buku_table
Migrated:  2024_02_10_123456_create_buku_table (50.23ms)

d. Verifikasi di phpMyAdmin

  1. Buka phpMyAdmin
  2. Pilih database perpustakaan_laravel
  3. Lihat tabel buku sudah terbuat
  4. Cek struktur tabel (columns, types, indexes)

e. Cek Status Migration

php artisan migrate:status

Output:

Migration name .................... Batch / Status
2019_12_14_000001_create_personal_access_tokens_table .. [1] Ran
2024_02_10_123456_create_buku_table ................... [1] Ran

4. PRAKTIKUM 3: Migration Tabel Anggota

Tujuan

Membuat migration untuk tabel anggota perpustakaan.

Langkah-langkah

a. Buat Migration

php artisan make:migration create_anggota_table

b. Edit Migration File

File: database/migrations/2024_02_10_123457_create_anggota_table.php

<?php
 
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
 
return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('anggota', function (Blueprint $table) {
            $table->id();
            $table->string('kode_anggota', 20)->unique();
            $table->string('nama', 100);
            $table->string('email', 100)->unique();
            $table->string('telepon', 15);
            $table->text('alamat');
            $table->date('tanggal_lahir');
            $table->enum('jenis_kelamin', ['Laki-laki', 'Perempuan']);
            $table->string('pekerjaan', 50)->nullable();
            $table->date('tanggal_daftar');
            $table->enum('status', ['Aktif', 'Nonaktif'])->default('Aktif');
            $table->timestamps();
        });
    }
 
    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('anggota');
    }
};

c. Jalankan Migration

php artisan migrate

Output:

Migrating: 2024_02_10_123457_create_anggota_table
Migrated:  2024_02_10_123457_create_anggota_table (45.12ms)

d. Verifikasi

Cek di phpMyAdmin bahwa tabel anggota sudah terbuat dengan struktur yang benar.


5. PRAKTIKUM 4: Membuat Model Eloquent

Tujuan

Membuat Model untuk tabel buku dan anggota.

Langkah-langkah

a. Buat Model Buku

php artisan make:model Buku

Output:

Model created successfully.

b. Edit Model Buku

File: app/Models/Buku.php

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
 
class Buku extends Model
{
    use HasFactory;
 
    /**
     * Nama tabel yang digunakan oleh model ini.
     *
     * @var string
     */
    protected $table = 'buku';
 
    /**
     * Kolom yang dapat diisi secara mass assignment.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'kode_buku',
        'judul',
        'kategori',
        'pengarang',
        'penerbit',
        'tahun_terbit',
        'isbn',
        'harga',
        'stok',
        'deskripsi',
        'bahasa',
    ];
 
    /**
     * Tipe casting untuk atribut.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'tahun_terbit' => 'integer',
        'harga' => 'decimal:2',
        'stok' => 'integer',
    ];
 
    /**
     * Accessor untuk format harga.
     */
    public function getHargaFormatAttribute(): string
    {
        return 'Rp ' . number_format($this->harga, 0, ',', '.');
    }
 
    /**
     * Accessor untuk status ketersediaan.
     */
    public function getTersediaAttribute(): bool
    {
        return $this->stok > 0;
    }
 
    /**
     * Scope untuk filter buku tersedia.
     */
    public function scopeTersedia($query)
    {
        return $query->where('stok', '>', 0);
    }
 
    /**
     * Scope untuk filter berdasarkan kategori.
     */
    public function scopeKategori($query, $kategori)
    {
        return $query->where('kategori', $kategori);
    }
}

c. Buat Model Anggota

php artisan make:model Anggota

d. Edit Model Anggota

File: app/Models/Anggota.php

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Carbon\Carbon;
 
class Anggota extends Model
{
    use HasFactory;
 
    /**
     * Nama tabel yang digunakan oleh model ini.
     *
     * @var string
     */
    protected $table = 'anggota';
 
    /**
     * Kolom yang dapat diisi secara mass assignment.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'kode_anggota',
        'nama',
        'email',
        'telepon',
        'alamat',
        'tanggal_lahir',
        'jenis_kelamin',
        'pekerjaan',
        'tanggal_daftar',
        'status',
    ];
 
    /**
     * Tipe casting untuk atribut.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'tanggal_lahir' => 'date',
        'tanggal_daftar' => 'date',
    ];
 
    /**
     * Accessor untuk menghitung umur.
     */
    public function getUmurAttribute(): int
    {
        return Carbon::parse($this->tanggal_lahir)->age;
    }
 
    /**
     * Accessor untuk lama menjadi anggota (dalam hari).
     */
    public function getLamaAnggotaAttribute(): int
    {
        return Carbon::parse($this->tanggal_daftar)->diffInDays(now());
    }
 
    /**
     * Scope untuk filter anggota aktif.
     */
    public function scopeAktif($query)
    {
        return $query->where('status', 'Aktif');
    }
 
    /**
     * Scope untuk filter berdasarkan jenis kelamin.
     */
    public function scopeJenisKelamin($query, $jenisKelamin)
    {
        return $query->where('jenis_kelamin', $jenisKelamin);
    }
}

e. Test Model via Tinker

Laravel Tinker adalah REPL (Read-Eval-Print-Loop) untuk testing code PHP secara interaktif.

php artisan tinker

Di Tinker, test model:

// Test Buku Model
>>> $buku = new App\Models\Buku();
>>> $buku->judul = "Test Laravel 12";
>>> $buku->kategori = "Programming";
>>> $buku->harga = 150000;
>>> $buku  // Lihat object
 
// Test Anggota Model
>>> $anggota = new App\Models\Anggota();
>>> $anggota->nama = "John Doe";
>>> $anggota->email = "john@example.com";
>>> $anggota  // Lihat object
 
// Keluar dari tinker
>>> exit

6. PRAKTIKUM 5: Database Seeding

Tujuan

Membuat seeder untuk mengisi data sample buku dan anggota.

Langkah-langkah

a. Buat Seeder Buku

php artisan make:seeder BukuSeeder

b. Edit BukuSeeder

File: database/seeders/BukuSeeder.php

<?php
 
namespace Database\Seeders;
 
use Illuminate\Database\Seeder;
use App\Models\Buku;
 
class BukuSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        $bukuList = [
            [
                'kode_buku' => 'BK-001',
                'judul' => 'Laravel 12 untuk Pemula',
                'kategori' => 'Programming',
                'pengarang' => 'John Doe',
                'penerbit' => 'Tech Publisher',
                'tahun_terbit' => 2024,
                'isbn' => '978-602-1234-56-1',
                'harga' => 150000,
                'stok' => 20,
                'deskripsi' => 'Buku panduan lengkap Laravel 12 dari dasar hingga mahir',
                'bahasa' => 'Indonesia',
            ],
            [
                'kode_buku' => 'BK-002',
                'judul' => 'MySQL Advanced Techniques',
                'kategori' => 'Database',
                'pengarang' => 'Jane Smith',
                'penerbit' => 'Data Press',
                'tahun_terbit' => 2023,
                'isbn' => '978-602-1234-56-2',
                'harga' => 175000,
                'stok' => 15,
                'deskripsi' => 'Teknik advanced untuk optimasi MySQL database',
                'bahasa' => 'Inggris',
            ],
            [
                'kode_buku' => 'BK-003',
                'judul' => 'Modern Web Design',
                'kategori' => 'Web Design',
                'pengarang' => 'Ahmad Yani',
                'penerbit' => 'Creative Media',
                'tahun_terbit' => 2024,
                'isbn' => '978-602-1234-56-3',
                'harga' => 120000,
                'stok' => 25,
                'deskripsi' => 'Prinsip dan praktik desain web modern',
                'bahasa' => 'Indonesia',
            ],
            [
                'kode_buku' => 'BK-004',
                'judul' => 'Network Security Fundamentals',
                'kategori' => 'Networking',
                'pengarang' => 'Robert Johnson',
                'penerbit' => 'Security Press',
                'tahun_terbit' => 2023,
                'isbn' => '978-602-1234-56-4',
                'harga' => 200000,
                'stok' => 10,
                'deskripsi' => 'Dasar-dasar keamanan jaringan komputer',
                'bahasa' => 'Inggris',
            ],
            [
                'kode_buku' => 'BK-005',
                'judul' => 'Data Science dengan Python',
                'kategori' => 'Data Science',
                'pengarang' => 'Siti Nurhaliza',
                'penerbit' => 'Analytics Publisher',
                'tahun_terbit' => 2024,
                'isbn' => '978-602-1234-56-5',
                'harga' => 180000,
                'stok' => 18,
                'deskripsi' => 'Panduan praktis data science menggunakan Python',
                'bahasa' => 'Indonesia',
            ],
            [
                'kode_buku' => 'BK-006',
                'judul' => 'PHP 8 Programming',
                'kategori' => 'Programming',
                'pengarang' => 'Budi Raharjo',
                'penerbit' => 'Code House',
                'tahun_terbit' => 2023,
                'isbn' => '978-602-1234-56-6',
                'harga' => 130000,
                'stok' => 0,
                'deskripsi' => 'Fitur-fitur terbaru PHP 8',
                'bahasa' => 'Indonesia',
            ],
            [
                'kode_buku' => 'BK-007',
                'judul' => 'PostgreSQL Administration',
                'kategori' => 'Database',
                'pengarang' => 'David Wilson',
                'penerbit' => 'Database Pro',
                'tahun_terbit' => 2024,
                'isbn' => '978-602-1234-56-7',
                'harga' => 195000,
                'stok' => 12,
                'deskripsi' => 'Administrasi dan optimasi PostgreSQL',
                'bahasa' => 'Inggris',
            ],
            [
                'kode_buku' => 'BK-008',
                'judul' => 'React & Next.js Development',
                'kategori' => 'Programming',
                'pengarang' => 'Sarah Anderson',
                'penerbit' => 'Frontend Press',
                'tahun_terbit' => 2024,
                'isbn' => '978-602-1234-56-8',
                'harga' => 165000,
                'stok' => 22,
                'deskripsi' => 'Membangun aplikasi modern dengan React dan Next.js',
                'bahasa' => 'Inggris',
            ],
        ];
 
        foreach ($bukuList as $buku) {
            Buku::create($buku);
        }
    }
}

c. Buat Seeder Anggota

php artisan make:seeder AnggotaSeeder

d. Edit AnggotaSeeder

File: database/seeders/AnggotaSeeder.php

<?php
 
namespace Database\Seeders;
 
use Illuminate\Database\Seeder;
use App\Models\Anggota;
use Carbon\Carbon;
 
class AnggotaSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        $anggotaList = [
            [
                'kode_anggota' => 'AGT-001',
                'nama' => 'Budi Santoso',
                'email' => 'budi.santoso@email.com',
                'telepon' => '081234567890',
                'alamat' => 'Jl. Merdeka No. 10, Jakarta Pusat',
                'tanggal_lahir' => '1995-05-15',
                'jenis_kelamin' => 'Laki-laki',
                'pekerjaan' => 'Mahasiswa',
                'tanggal_daftar' => '2024-01-10',
                'status' => 'Aktif',
            ],
            [
                'kode_anggota' => 'AGT-002',
                'nama' => 'Siti Nurhaliza',
                'email' => 'siti.nur@email.com',
                'telepon' => '081234567891',
                'alamat' => 'Jl. Sudirman No. 25, Bandung',
                'tanggal_lahir' => '1998-08-20',
                'jenis_kelamin' => 'Perempuan',
                'pekerjaan' => 'Pegawai Swasta',
                'tanggal_daftar' => '2024-01-15',
                'status' => 'Aktif',
            ],
            [
                'kode_anggota' => 'AGT-003',
                'nama' => 'Ahmad Dhani',
                'email' => 'ahmad.dhani@email.com',
                'telepon' => '081234567892',
                'alamat' => 'Jl. Gatot Subroto No. 5, Surabaya',
                'tanggal_lahir' => '1992-03-10',
                'jenis_kelamin' => 'Laki-laki',
                'pekerjaan' => 'Dosen',
                'tanggal_daftar' => '2024-02-01',
                'status' => 'Aktif',
            ],
            [
                'kode_anggota' => 'AGT-004',
                'nama' => 'Dewi Lestari',
                'email' => 'dewi.lestari@email.com',
                'telepon' => '081234567893',
                'alamat' => 'Jl. Ahmad Yani No. 30, Yogyakarta',
                'tanggal_lahir' => '2000-12-05',
                'jenis_kelamin' => 'Perempuan',
                'pekerjaan' => 'Mahasiswa',
                'tanggal_daftar' => '2024-02-10',
                'status' => 'Aktif',
            ],
            [
                'kode_anggota' => 'AGT-005',
                'nama' => 'Rizky Febian',
                'email' => 'rizky.feb@email.com',
                'telepon' => '081234567894',
                'alamat' => 'Jl. Diponegoro No. 15, Semarang',
                'tanggal_lahir' => '1997-07-18',
                'jenis_kelamin' => 'Laki-laki',
                'pekerjaan' => 'Wiraswasta',
                'tanggal_daftar' => '2023-12-15',
                'status' => 'Nonaktif',
            ],
        ];
 
        foreach ($anggotaList as $anggota) {
            Anggota::create($anggota);
        }
    }
}

e. Register Seeder di DatabaseSeeder

File: database/seeders/DatabaseSeeder.php

<?php
 
namespace Database\Seeders;
 
use Illuminate\Database\Seeder;
 
class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     */
    public function run(): void
    {
        $this->call([
            BukuSeeder::class,
            AnggotaSeeder::class,
        ]);
    }
}

f. Jalankan Seeder

# Jalankan semua seeder
php artisan db:seed
 
# Atau jalankan seeder tertentu
php artisan db:seed --class=BukuSeeder
php artisan db:seed --class=AnggotaSeeder

Output:

Seeding: Database\Seeders\BukuSeeder
Seeded:  Database\Seeders\BukuSeeder (150.23ms)
Seeding: Database\Seeders\AnggotaSeeder
Seeded:  Database\Seeders\AnggotaSeeder (45.12ms)

g. Verifikasi Data

  1. Buka phpMyAdmin
  2. Pilih database perpustakaan_laravel
  3. Cek tabel buku - harus ada 8 records
  4. Cek tabel anggota - harus ada 5 records

h. Fresh Migration dengan Seed

Jika ingin reset dan seed ulang:

php artisan migrate:fresh --seed

Command ini akan:

  1. Drop semua tabel
  2. Re-run semua migration
  3. Jalankan semua seeder

7. PRAKTIKUM 6: Testing CRUD dengan Tinker

Tujuan

Melakukan testing CRUD operations menggunakan Eloquent ORM via Tinker.

Langkah-langkah

a. Buka Tinker

php artisan tinker

b. READ Operations

// 1. Get all buku
>>> $bukus = App\Models\Buku::all();
>>> $bukus->count()  // Jumlah buku
 
// 2. Get first buku
>>> $buku = App\Models\Buku::first();
>>> $buku->judul
 
// 3. Find by ID
>>> $buku = App\Models\Buku::find(1);
>>> $buku->judul
>>> $buku->harga_format  // Accessor
 
// 4. Find by kode_buku
>>> $buku = App\Models\Buku::where('kode_buku', 'BK-001')->first();
>>> $buku->judul
 
// 5. Filter kategori Programming
>>> $bukus = App\Models\Buku::where('kategori', 'Programming')->get();
>>> $bukus->count()
 
// 6. Filter buku tersedia (stok > 0)
>>> $bukus = App\Models\Buku::tersedia()->get();
>>> $bukus->pluck('judul')
 
// 7. Filter dengan scope
>>> $bukus = App\Models\Buku::kategori('Database')->get();
>>> $bukus->pluck('judul')
 
// 8. Chaining query
>>> $bukus = App\Models\Buku::where('kategori', 'Programming')
                             ->where('stok', '>', 10)
                             ->orderBy('harga', 'desc')
                             ->get();

c. CREATE Operations

// 1. Create dengan save()
>>> $buku = new App\Models\Buku();
>>> $buku->kode_buku = 'BK-009';
>>> $buku->judul = 'Vue.js 3 Complete Guide';
>>> $buku->kategori = 'Programming';
>>> $buku->pengarang = 'Sarah Chen';
>>> $buku->penerbit = 'Frontend Books';
>>> $buku->tahun_terbit = 2024;
>>> $buku->harga = 145000;
>>> $buku->stok = 15;
>>> $buku->bahasa = 'Inggris';
>>> $buku->save()
>>> $buku->id  // Auto increment
 
// 2. Create dengan mass assignment
>>> App\Models\Buku::create([
        'kode_buku' => 'BK-010',
        'judul' => 'Docker & Kubernetes',
        'kategori' => 'Programming',
        'pengarang' => 'Michael Johnson',
        'penerbit' => 'DevOps Press',
        'tahun_terbit' => 2024,
        'harga' => 210000,
        'stok' => 8,
        'bahasa' => 'Inggris'
    ])
 
// Verifikasi
>>> App\Models\Buku::count()  // Should be 10 now

d. UPDATE Operations

// 1. Update dengan save()
>>> $buku = App\Models\Buku::find(1);
>>> $buku->stok = 25;
>>> $buku->harga = 155000;
>>> $buku->save()
 
// 2. Update dengan update()
>>> $buku = App\Models\Buku::find(2);
>>> $buku->update([
        'stok' => 20,
        'harga' => 180000
    ])
 
// 3. Update multiple records
>>> App\Models\Buku::where('kategori', 'Programming')
                   ->update(['stok' => 30])
 
// Verifikasi
>>> $buku = App\Models\Buku::find(1);
>>> $buku->stok  // Should be 25

e. DELETE Operations

// 1. Delete dengan delete()
>>> $buku = App\Models\Buku::find(10);
>>> $buku->delete()
 
// 2. Delete dengan destroy()
>>> App\Models\Buku::destroy(9)
 
// 3. Delete multiple IDs
>>> App\Models\Buku::destroy([7, 8])  // Jangan jalankan ini!
 
// 4. Delete with condition
>>> App\Models\Buku::where('stok', 0)->delete()  // Hapus buku habis
 
// Verifikasi
>>> App\Models\Buku::count()

f. Testing Anggota Model

// READ
>>> $anggotas = App\Models\Anggota::all();
>>> $anggotas->count()
 
>>> $anggota = App\Models\Anggota::first();
>>> $anggota->nama
>>> $anggota->umur  // Accessor
>>> $anggota->lama_anggota  // Accessor
 
// Filter aktif
>>> $aktif = App\Models\Anggota::aktif()->get();
>>> $aktif->pluck('nama')
 
// CREATE
>>> App\Models\Anggota::create([
        'kode_anggota' => 'AGT-006',
        'nama' => 'Testing User',
        'email' => 'test@example.com',
        'telepon' => '081234567899',
        'alamat' => 'Jl. Test No. 1',
        'tanggal_lahir' => '2000-01-01',
        'jenis_kelamin' => 'Laki-laki',
        'pekerjaan' => 'Tester',
        'tanggal_daftar' => today(),
        'status' => 'Aktif'
    ])
 
// Keluar dari Tinker
>>> exit

8. PRAKTIKUM 7: Rollback & Modify Migration

Tujuan

Memahami cara rollback migration dan memodifikasi struktur tabel.

Langkah-langkah

a. Cek Status Migration

php artisan migrate:status

b. Rollback Migration Terakhir

php artisan migrate:rollback

Ini akan rollback batch migration terakhir (anggota).

Cek di phpMyAdmin - tabel anggota hilang, tabel buku masih ada.

c. Rollback Step

# Rollback 1 step
php artisan migrate:rollback --step=1
 
# Rollback 2 step
php artisan migrate:rollback --step=2

d. Re-migrate

php artisan migrate

e. Tambah Column ke Tabel Buku

Buat migration baru untuk modify:

php artisan make:migration add_penerbit_detail_to_buku_table

Edit migration file:

<?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::table('buku', function (Blueprint $table) {
            $table->string('negara_penerbit', 50)->nullable()->after('penerbit');
            $table->string('kota_penerbit', 50)->nullable()->after('negara_penerbit');
        });
    }
 
    public function down(): void
    {
        Schema::table('buku', function (Blueprint $table) {
            $table->dropColumn(['negara_penerbit', 'kota_penerbit']);
        });
    }
};

Jalankan migration:

php artisan migrate

Cek di phpMyAdmin - tabel buku sekarang punya 2 kolom baru.

f. Refresh Migration

# Drop all tables dan re-run semua migration
php artisan migrate:fresh
 
# Dengan seeding
php artisan migrate:fresh --seed

âš ī¸ Warning: migrate:fresh akan menghapus SEMUA data!


9. PRAKTIKUM 8: Route Testing CRUD

Tujuan

Membuat route sederhana untuk testing CRUD via browser.

Langkah-langkah

a. Edit routes/web.php

<?php
 
use Illuminate\Support\Facades\Route;
use App\Models\Buku;
use App\Models\Anggota;
 
Route::get('/', function () {
    return view('welcome');
});
 
// ========== TESTING BUKU ==========
 
// List all buku
Route::get('/buku', function () {
    $bukus = Buku::all();
    
    $html = '<h1>Daftar Buku</h1>';
    $html .= '<a href="/buku/create">Tambah Buku</a><br /><br />';
    $html .= '<table border="1" cellpadding="10">';
    $html .= '<tr>
                <th>ID</th>
                <th>Kode</th>
                <th>Judul</th>
                <th>Kategori</th>
                <th>Harga</th>
                <th>Stok</th>
                <th>Aksi</th>
              </tr>';
    
    foreach ($bukus as $buku) {
        $html .= '<tr>';
        $html .= '<td>' . $buku->id . '</td>';
        $html .= '<td>' . $buku->kode_buku . '</td>';
        $html .= '<td>' . $buku->judul . '</td>';
        $html .= '<td>' . $buku->kategori . '</td>';
        $html .= '<td>' . $buku->harga_format . '</td>';
        $html .= '<td>' . $buku->stok . '</td>';
        $html .= '<td>
                    <a href="/buku/' . $buku->id . '">Detail</a> | 
                    <a href="/buku/' . $buku->id . '/edit">Edit</a>
                  </td>';
        $html .= '</tr>';
    }
    
    $html .= '</table>';
    
    return $html;
});
 
// Show single buku
Route::get('/buku/{id}', function ($id) {
    $buku = Buku::findOrFail($id);
    
    $html = '<h1>Detail Buku</h1>';
    $html .= '<a href="/buku">Kembali</a><br /><br />';
    $html .= '<table border="1" cellpadding="10">';
    $html .= '<tr><th>Field</th><th>Value</th></tr>';
    $html .= '<tr><td>ID</td><td>' . $buku->id . '</td></tr>';
    $html .= '<tr><td>Kode Buku</td><td>' . $buku->kode_buku . '</td></tr>';
    $html .= '<tr><td>Judul</td><td>' . $buku->judul . '</td></tr>';
    $html .= '<tr><td>Kategori</td><td>' . $buku->kategori . '</td></tr>';
    $html .= '<tr><td>Pengarang</td><td>' . $buku->pengarang . '</td></tr>';
    $html .= '<tr><td>Penerbit</td><td>' . $buku->penerbit . '</td></tr>';
    $html .= '<tr><td>Tahun</td><td>' . $buku->tahun_terbit . '</td></tr>';
    $html .= '<tr><td>ISBN</td><td>' . $buku->isbn . '</td></tr>';
    $html .= '<tr><td>Harga</td><td>' . $buku->harga_format . '</td></tr>';
    $html .= '<tr><td>Stok</td><td>' . $buku->stok . '</td></tr>';
    $html .= '<tr><td>Tersedia?</td><td>' . ($buku->tersedia ? 'Ya' : 'Tidak') . '</td></tr>';
    $html .= '<tr><td>Created</td><td>' . $buku->created_at . '</td></tr>';
    $html .= '<tr><td>Updated</td><td>' . $buku->updated_at . '</td></tr>';
    $html .= '</table>';
    
    return $html;
});
 
// ========== TESTING ANGGOTA ==========
 
// List all anggota
Route::get('/anggota', function () {
    $anggotas = Anggota::all();
    
    $html = '<h1>Daftar Anggota</h1>';
    $html .= '<table border="1" cellpadding="10">';
    $html .= '<tr>
                <th>ID</th>
                <th>Kode</th>
                <th>Nama</th>
                <th>Email</th>
                <th>Umur</th>
                <th>Status</th>
                <th>Aksi</th>
              </tr>';
    
    foreach ($anggotas as $anggota) {
        $html .= '<tr>';
        $html .= '<td>' . $anggota->id . '</td>';
        $html .= '<td>' . $anggota->kode_anggota . '</td>';
        $html .= '<td>' . $anggota->nama . '</td>';
        $html .= '<td>' . $anggota->email . '</td>';
        $html .= '<td>' . $anggota->umur . ' tahun</td>';
        $html .= '<td>' . $anggota->status . '</td>';
        $html .= '<td><a href="/anggota/' . $anggota->id . '">Detail</a></td>';
        $html .= '</tr>';
    }
    
    $html .= '</table>';
    
    return $html;
});
 
// Show single anggota
Route::get('/anggota/{id}', function ($id) {
    $anggota = Anggota::findOrFail($id);
    
    $html = '<h1>Detail Anggota</h1>';
    $html .= '<a href="/anggota">Kembali</a><br /><br />';
    $html .= '<table border="1" cellpadding="10">';
    $html .= '<tr><th>Field</th><th>Value</th></tr>';
    $html .= '<tr><td>Kode Anggota</td><td>' . $anggota->kode_anggota . '</td></tr>';
    $html .= '<tr><td>Nama</td><td>' . $anggota->nama . '</td></tr>';
    $html .= '<tr><td>Email</td><td>' . $anggota->email . '</td></tr>';
    $html .= '<tr><td>Telepon</td><td>' . $anggota->telepon . '</td></tr>';
    $html .= '<tr><td>Alamat</td><td>' . $anggota->alamat . '</td></tr>';
    $html .= '<tr><td>Tanggal Lahir</td><td>' . $anggota->tanggal_lahir->format('d-m-Y') . '</td></tr>';
    $html .= '<tr><td>Umur</td><td>' . $anggota->umur . ' tahun</td></tr>';
    $html .= '<tr><td>Jenis Kelamin</td><td>' . $anggota->jenis_kelamin . '</td></tr>';
    $html .= '<tr><td>Pekerjaan</td><td>' . $anggota->pekerjaan . '</td></tr>';
    $html .= '<tr><td>Tanggal Daftar</td><td>' . $anggota->tanggal_daftar->format('d-m-Y') . '</td></tr>';
    $html .= '<tr><td>Lama Anggota</td><td>' . $anggota->lama_anggota . ' hari</td></tr>';
    $html .= '<tr><td>Status</td><td>' . $anggota->status . '</td></tr>';
    $html .= '</table>';
    
    return $html;
});
 
// Testing Scope & Query
Route::get('/test-query', function () {
    $html = '<h1>Testing Query Eloquent</h1>';
    
    // Buku tersedia
    $tersedia = Buku::tersedia()->get();
    $html .= '<h3>Buku Tersedia (Stok > 0): ' . $tersedia->count() . '</h3>';
    $html .= '<ul>';
    foreach ($tersedia as $buku) {
        $html .= '<li>' . $buku->judul . ' (Stok: ' . $buku->stok . ')</li>';
    }
    $html .= '</ul>';
    
    // Buku Programming
    $programming = Buku::kategori('Programming')->get();
    $html .= '<h3>Buku Programming: ' . $programming->count() . '</h3>';
    $html .= '<ul>';
    foreach ($programming as $buku) {
        $html .= '<li>' . $buku->judul . '</li>';
    }
    $html .= '</ul>';
    
    // Anggota Aktif
    $aktif = Anggota::aktif()->get();
    $html .= '<h3>Anggota Aktif: ' . $aktif->count() . '</h3>';
    $html .= '<ul>';
    foreach ($aktif as $anggota) {
        $html .= '<li>' . $anggota->nama . ' (' . $anggota->email . ')</li>';
    }
    $html .= '</ul>';
    
    return $html;
});

b. Testing Routes

  1. Akses: http://localhost:8000/buku
    • Lihat daftar semua buku
  2. Akses: http://localhost:8000/buku/1
    • Lihat detail buku ID 1
  3. Akses: http://localhost:8000/anggota
    • Lihat daftar semua anggota
  4. Akses: http://localhost:8000/anggota/1
    • Lihat detail anggota ID 1
  5. Akses: http://localhost:8000/test-query
    • Lihat hasil query dengan scope

E. TUGAS

Tugas 1: Migration Tabel Kategori (40%)

Instruksi: Buat migration untuk tabel kategori yang akan menyimpan kategori buku secara terpisah (normalisasi database).

Spesifikasi:

  1. Buat Migration:
php artisan make:migration create_kategori_table
  1. Struktur Tabel:

    • id: bigint, primary key, auto increment
    • nama_kategori: string(50), unique, not null
    • deskripsi: text, nullable
    • icon: string(50), nullable (untuk icon Bootstrap)
    • warna: string(20), nullable (untuk badge color)
    • timestamps: created_at, updated_at
  2. Buat Model:

php artisan make:model Kategori
  1. Fillable di Model:

    • nama_kategori
    • deskripsi
    • icon
    • warna
  2. Buat Seeder: Data kategori:

    • Programming (icon: code-slash, warna: primary)
    • Database (icon: database, warna: success)
    • Web Design (icon: palette, warna: info)
    • Networking (icon: wifi, warna: warning)
    • Data Science (icon: graph-up, warna: danger)

Tugas 2: Model Accessor & Scope (60%)

Instruksi: Tambahkan accessor dan scope ke Model Buku dan Anggota.

Spesifikasi:

A. Model Buku - Tambahkan:

  1. Accessor status_stok_badge:

    • Return HTML badge berdasarkan stok:
      • Stok = 0: <span class="badge bg-danger">Habis</span>
      • Stok 1-5: <span class="badge bg-warning">Menipis</span>
      • Stok 6-15: <span class="badge bg-info">Sedang</span>
      • Stok > 15: <span class="badge bg-success">Aman</span>
  2. Accessor tahun_label:

    • Return "Buku Baru" jika tahun >= 2024
    • Return "Buku Lama" jika tahun < 2024
  3. Scope stokMenipis():

    • Filter buku dengan stok < 5
  4. Scope hargaRange($min, $max):

    • Filter buku dengan harga antara $min dan $max
  5. Scope terbaru():

    • Filter buku dengan tahun_terbit >= 2024

B. Model Anggota - Tambahkan:

  1. Accessor status_badge:

    • Aktif: <span class="badge bg-success">Aktif</span>
    • Nonaktif: <span class="badge bg-secondary">Nonaktif</span>
  2. Accessor kategori_usia:

    • Return "Remaja" jika umur < 20
    • Return "Dewasa" jika umur 20-50
    • Return "Senior" jika umur > 50
  3. Scope jenisKelamin($jk):

    • Filter berdasarkan jenis kelamin
  4. Scope terdaftarBulanIni():

    • Filter anggota yang terdaftar di bulan ini

C. Testing Route:

Buat route /test-accessor-scope yang menampilkan:

  • Buku dengan status_stok_badge
  • Buku terbaru (scope)
  • Buku stok menipis (scope)
  • Anggota dengan status_badge
  • Anggota dengan kategori_usia
  • Anggota terdaftar bulan ini (scope)

Template Code:

// Model Buku - Accessor
public function getStatusStokBadgeAttribute(): string
{
    // TODO: Implement logic
}
 
// Model Buku - Scope
public function scopeStokMenipis($query)
{
    // TODO: Implement
}
 
// Testing Route
Route::get('/test-accessor-scope', function () {
    $html = '<h1>Testing Accessor & Scope</h1>';
    
    // TODO: Test semua accessor dan scope
    
    return $html;
});

Submission:

  • Format: Link repository GitHub (sertakan screenshot hasil migration, seeder, dan route testing di README)
  • Deadline: Pertemuan 11
  • Upload ke: Ngaji UIN Gusdur (submit link repository GitHub)

F. EVALUASI

1. Kuis Singkat (Dikerjakan di Kelas)

Soal Pilihan Ganda:

  1. Perintah untuk membuat migration baru adalah:

    • A. php artisan create:migration
    • B. php artisan make:migration
    • C. php artisan migration:create
    • D. php artisan new:migration
  2. Method di migration untuk membatalkan perubahan adalah:

    • A. reverse()
    • B. rollback()
    • C. down()
    • D. undo()
  3. Perintah untuk menjalankan semua migration adalah:

    • A. php artisan migrate
    • B. php artisan db:migrate
    • C. php artisan migration:run
    • D. php artisan run:migrate
  4. Untuk membuat primary key auto increment gunakan:

    • A. $table->primaryKey()
    • B. $table->id()
    • C. $table->autoIncrement()
    • D. $table->pk()
  5. ORM yang digunakan Laravel adalah:

    • A. Doctrine
    • B. Eloquent
    • C. Propel
    • D. RedBean
  6. Perintah untuk rollback migration terakhir:

    • A. php artisan migrate:undo
    • B. php artisan migrate:rollback
    • C. php artisan migrate:revert
    • D. php artisan migrate:back
  7. Property untuk mass assignment whitelist adalah:

    • A. $allowed
    • B. $fillable
    • C. $permitted
    • D. $writable
  8. Untuk membuat Model dan Migration sekaligus:

    • A. php artisan make:model Buku -m
    • B. php artisan make:model Buku --migration
    • C. php artisan make:model Buku -c
    • D. A dan B benar
  9. Method untuk mencari record by primary key:

    • A. Model::get($id)
    • B. Model::search($id)
    • C. Model::find($id)
    • D. Model::where('id', $id)
  10. Perintah untuk fresh migrate + seed:

    • A. php artisan migrate:fresh --seed
    • B. php artisan db:fresh --seed
    • C. php artisan fresh:seed
    • D. php artisan migrate:seed --fresh

Soal Essay:

  1. Jelaskan perbedaan migration dengan cara manual membuat tabel SQL! Apa keuntungan menggunakan migration? (10 poin)

    Jawaban tersedia di dokumen kunci jawaban dosen.

  2. Apa fungsi up() dan down() dalam migration? Berikan contoh! (10 poin)

    Jawaban tersedia di dokumen kunci jawaban dosen.

  3. Jelaskan apa itu Eloquent ORM dan apa keuntungannya dibanding raw SQL! (10 poin)

    Jawaban tersedia di dokumen kunci jawaban dosen.

  4. Buatlah migration untuk membuat tabel 'penerbit' dengan field: id, nama_penerbit, alamat, telepon! (15 poin)

    Jawaban tersedia di dokumen kunci jawaban dosen.

  5. Apa itu Mass Assignment dan mengapa perlu $fillable/$guarded? (10 poin)

    Jawaban tersedia di dokumen kunci jawaban dosen.


2. Checklist Kompetensi

Self-Assessment:

NoKompetensiBelumCukupMahir
1Membuat migration file○○○
2Mendefinisikan schema dengan Blueprint○○○
3Menjalankan migration○○○
4Rollback migration○○○
5Membuat Model Eloquent○○○
6Menggunakan fillable/guarded○○○
7CRUD dengan Eloquent○○○
8Membuat accessor○○○
9Membuat scope○○○
10Database seeding○○○

Target: Minimal "Cukup" untuk semua poin sebelum lanjut ke pertemuan 11.


G. REFERENSI

1. Dokumentasi Resmi Laravel 12

2. Tutorial & Course

  • Laravel Daily - Migrations Best Practices
  • Laracasts - Eloquent Techniques
  • Laravel News - Database Tips

3. Tools


H. CATATAN PENTING

Untuk Mahasiswa:

1. Best Practices Migration

✅ DO:

// Nama file descriptive
create_buku_table
add_status_to_anggota_table
 
// Gunakan Blueprint methods
$table->string('judul', 200);
$table->decimal('harga', 10, 2);
 
// Selalu implement down()
public function down(): void {
    Schema::dropIfExists('buku');
}
 
// Commit migration files ke Git

❌ DON'T:

// Jangan edit migration yang sudah di-run di production
// Buat migration baru untuk perubahan
 
// Jangan hardcode values
$table->string('status')->default('aktif'); // ❌
$table->enum('status', ['aktif', 'nonaktif'])->default('aktif'); //
 
// Jangan lupa timestamps
// $table->timestamps(); // Sangat recommended

2. Best Practices Model

✅ DO:

// Selalu define fillable atau guarded
protected $fillable = ['judul', 'harga'];
 
// Gunakan casting
protected $casts = [
    'harga' => 'decimal:2',
    'tanggal' => 'date',
];
 
// Buat accessor untuk format
public function getHargaFormatAttribute() {
    return 'Rp ' . number_format($this->harga);
}
 
// Gunakan scope untuk query berulang
public function scopeAktif($query) {
    return $query->where('status', 'aktif');
}

❌ DON'T:

// Jangan biarkan mass assignment terbuka
protected $guarded = []; // ❌ Dangerous!
 
// Jangan query di view
// @foreach(Buku::all() as $buku) // ❌
// Gunakan di controller/route

3. Common Errors & Solutions

Error: "SQLSTATE[42S01]: Base table or view already exists"

# Solution: Table sudah ada
php artisan migrate:fresh  # Drop all tables

Error: "Add [field_name] to fillable property"

// Solution: Tambahkan ke $fillable
protected $fillable = ['field_name'];

Error: "Class 'App\Models\Buku' not found"

# Solution: Clear cache
php artisan optimize:clear
composer dump-autoload

I. PERSIAPAN PERTEMUAN 11

Topik Pertemuan 11: Controller & View (MVC Pattern)

Preview Materi:

  1. Controller responsibility
  2. Routing advanced
  3. Blade templating engine
  4. Passing data to view
  5. Layout & components

Yang Perlu Disiapkan:

  1. Migration & Model buku dan anggota sudah jalan
  2. Pahami relasi Model-Database
  3. Data sudah ter-seed dengan baik
  4. Laravel project siap digunakan

Pre-reading:


Selamat Belajar! 🚀📚

End of Module - Pertemuan 10

Next: Pertemuan 11 - Controller & View (MVC Pattern)