BAB 33: Seeder dan Factory

Mengisi database dengan data sampel realistis menggunakan seeder dan factory agar pengujian fitur lebih cepat dan konsisten.

CRUD sudah selesai — create, read, update, delete semuanya berfungsi. Tapi ada satu masalah praktis yang langsung terasa: setiap kali menjalankan migrate:fresh untuk mereset database, semua data hilang. Kamu harus kembali membuat catatan satu per satu lewat form, membuang waktu yang seharusnya dipakai untuk mengerjakan fitur berikutnya.

Di sinilah seeder dan factory berperan. Seeder adalah class PHP yang bertugas mengisi database secara otomatis. Factory adalah blueprint untuk membuat data realistis menggunakan library Faker — termasuk teks, angka, tanggal, dan berbagai format data lainnya. Kombinasi keduanya memungkinkan database terisi dengan puluhan data yang masuk akal hanya dari satu perintah.

Menambahkan HasFactory ke Model

Sebelum factory bisa digunakan, model Catatan perlu menggunakan trait HasFactory. Buka app/Models/Catatan.php dan tambahkan:

// app/Models/Catatan.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Catatan extends Model
{
    use HasFactory;

    protected $table = 'catatan';

    protected $fillable = [
        'user_id',
        'judul',
        'isi',
        'prioritas',
        'selesai',
    ];

    protected function casts(): array
    {
        return [
            'selesai' => 'boolean',
        ];
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

Trait HasFactory menghubungkan model ke class factory-nya. Tanpa trait ini, pemanggilan Catatan::factory() akan gagal dengan error.

Membuat Factory

Buat factory menggunakan Artisan:

php artisan make:factory CatatanFactory --model=Catatan

File baru muncul di database/factories/CatatanFactory.php. Isi method definition() dengan blueprint atribut — ini adalah nilai default yang akan dihasilkan setiap kali factory digunakan:

// database/factories/CatatanFactory.php
<?php

namespace Database\Factories;

use App\Models\Catatan;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;

class CatatanFactory extends Factory
{
    protected $model = Catatan::class;

    private static array $kontenIslami = [
        [
            'judul' => 'Belajar Laravel Middleware',
            'isi'   => 'Pelajari cara membuat middleware custom untuk validasi token JWT. Middleware adalah pintu masuk request — seperti muroqib yang menjaga pintu majelis, hanya yang layak yang boleh masuk.',
        ],
        [
            'judul' => 'Review Materi Eloquent Relationships',
            'isi'   => 'Ulangi materi hasMany, belongsTo, dan belongsToMany. Relasi antar tabel seperti silaturahmi antar manusia — harus dijaga dan dipahami dengan benar agar tidak putus.',
        ],
        [
            'judul' => 'Hafalan Surat Al-Kahfi Ayat 1-10',
            'isi'   => 'Targetkan hafal 2 ayat per hari. Baca dengan tartil dan pahami maknanya. Rasulullah SAW bersabda: barang siapa membaca sepuluh ayat awal Al-Kahfi, ia dilindungi dari fitnah Dajjal.',
        ],
        [
            'judul' => 'Persiapan Presentasi API REST',
            'isi'   => 'Siapkan slide untuk menjelaskan konsep stateless, resource endpoint, dan HTTP methods. Awali presentasi dengan basmalah agar ilmu yang disampaikan bermanfaat.',
        ],
        [
            'judul' => 'Tadabbur QS Al-Baqarah 286',
            'isi'   => 'Allah tidak membebani seseorang melainkan sesuai kesanggupannya. Gunakan ayat ini sebagai motivasi saat menghadapi bug yang sulit — setiap masalah pasti ada solusinya.',
        ],
        [
            'judul' => 'Reminder Sholat Dhuha Sebelum Coding',
            'isi'   => 'Luangkan 10 menit sebelum mulai kerja untuk sholat Dhuha 2 rakaat. Minta kemudahan rezeki dan ilmu yang berkah. Baru setelah itu buka IDE.',
        ],
        [
            'judul' => 'Tugas Refactor Controller Catatan',
            'isi'   => 'Pisahkan logic bisnis ke Service class agar controller lebih bersih. Prinsip single responsibility — seperti dalam kehidupan, setiap orang punya peran yang jelas.',
        ],
        [
            'judul' => 'Setup CI/CD untuk Proyek Masjid',
            'isi'   => 'Konfigurasi GitHub Actions untuk auto-deploy website masjid. Free tier sudah cukup. Proyek ini pro-bono, niatkan sebagai sedekah jariyah.',
        ],
        [
            'judul' => 'Optimasi Query N+1 Halaman Dashboard',
            'isi'   => 'Gunakan eager loading dengan with() untuk relasi user dan kategori di dashboard. Query yang efisien menghemat resource server — efisiensi adalah bagian dari tidak berlebih-lebihan.',
        ],
        [
            'judul' => 'Tadabbur Surat Al-Alaq Tentang Ilmu',
            'isi'   => 'Iqra bismirabbika alladzi khalaq. Ayat pertama yang turun adalah perintah membaca. Coding adalah salah satu bentuk iqra — membaca masalah dan mencari solusinya.',
        ],
    ];

    public function definition(): array
    {
        $konten = $this->faker->randomElement(self::$kontenIslami);

        return [
            'user_id'   => User::factory(),
            'judul'     => $konten['judul'],
            'isi'       => $konten['isi'],
            'prioritas' => $this->faker->randomElement(['rendah', 'sedang', 'tinggi']),
            'selesai'   => $this->faker->boolean(chanceOfGettingTrue: 30),
        ];
    }

    public function tinggi(): static
    {
        return $this->state(fn (array $attributes) => [
            'prioritas' => 'tinggi',
            'selesai'   => false,
        ]);
    }

    public function selesai(): static
    {
        return $this->state(fn (array $attributes) => [
            'selesai' => true,
        ]);
    }
}

Beberapa hal yang perlu diperhatikan di factory ini.

$kontenIslami adalah array konten statis yang mencerminkan konteks aplikasi — catatan seorang programmer muslim. Ini lebih bermakna daripada teks acak seperti lorem ipsum, sekaligus menjadi contoh bagaimana factory bisa disesuaikan dengan domain aplikasi yang sedang dibangun.

User::factory() sebagai nilai user_id artinya setiap kali factory digunakan tanpa menyuplai user, ia akan otomatis membuat User baru. Perilaku ini bisa di-override saat memanggil factory — yang akan kita lakukan di seeder.

fake()->boolean(chanceOfGettingTrue: 30) menghasilkan true 30% dari waktu. Mayoritas catatan berstatus belum selesai, yang lebih mencerminkan kondisi nyata.

Factory States

Method tinggi() dan selesai() adalah states — variasi dari definisi default yang bisa diaktifkan saat dibutuhkan. States membuat factory lebih ekspresif:

// Buat 5 catatan prioritas tinggi yang belum selesai
Catatan::factory()->count(5)->tinggi()->create();

// Buat 3 catatan yang sudah selesai
Catatan::factory()->count(3)->selesai()->create();

Membuat Seeder

Buat seeder menggunakan Artisan:

php artisan make:seeder CatatanSeeder

Isi database/seeders/CatatanSeeder.php dengan logika pengisian data:

// database/seeders/CatatanSeeder.php
<?php

namespace Database\Seeders;

use App\Models\Catatan;
use App\Models\User;
use Illuminate\Database\Seeder;

class CatatanSeeder extends Seeder
{
    public function run(): void
    {
        $user = User::firstOrCreate(
            ['email' => 'programmer@islamdev.test'],
            [
                'name'     => 'Ahmad Fauzi',
                'password' => bcrypt('password'),
            ]
        );

        Catatan::factory()->count(20)->for($user)->create();

        Catatan::factory()->count(5)->tinggi()->for($user)->create();

        Catatan::factory()->count(3)->selesai()->for($user)->create();
    }
}

User::firstOrCreate() memastikan user yang sama tidak duplikat jika seeder dijalankan ulang. Jika user dengan email tersebut sudah ada, ia diambil; jika belum, dibuat baru.

Method for($user) mengikat semua catatan yang dibuat ke user Ahmad Fauzi, menggantikan User::factory() yang ada di definisi factory. Hasilnya: 28 catatan terbagi menjadi 20 catatan biasa, 5 catatan prioritas tinggi yang belum selesai, dan 3 catatan yang sudah selesai.

Daftarkan ke DatabaseSeeder

File database/seeders/DatabaseSeeder.php adalah orkestrator — ia yang Laravel panggil secara default saat db:seed dijalankan. Daftarkan CatatanSeeder di sini:

// database/seeders/DatabaseSeeder.php
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        $this->call([
            CatatanSeeder::class,
        ]);
    }
}

Urutan dalam array call() penting jika ada dependensi antar seeder — seeder yang membutuhkan data dari seeder lain harus diletakkan setelahnya.

Menjalankan Seeder

Ada dua cara menjalankan seeder, tergantung situasi:

# Jalankan seeder tanpa menyentuh struktur tabel
php artisan db:seed

# Reset semua tabel, jalankan ulang migrasi, lalu seed — cara paling sering dipakai
php artisan migrate:fresh --seed

Perintah migrate:fresh --seed adalah kombinasi yang paling praktis selama pengembangan. Satu perintah untuk memulai dari kondisi database yang bersih dengan data yang siap diuji.

migrate:fresh menghapus semua tabel beserta seluruh datanya. Jangan jalankan di database production. Gunakan hanya di environment development atau staging.

Jalankan sekarang:

php artisan migrate:fresh --seed

Output yang diharapkan:

Dropping all tables ............................................ DONE

INFO  Running migrations.

0001_01_01_000000_create_users_table .......................... DONE
0001_01_01_000001_create_cache_table .......................... DONE
0001_01_01_000002_create_jobs_table ........................... DONE
2026_03_17_020947_create_catatans_table ........................ DONE

INFO  Seeding database.

Database\Seeders\CatatanSeeder ................................. DONE

Buka halaman /catatan di browser. Daftar catatan sekarang terisi dengan 28 data bertema programmer islami — tidak perlu lagi mengisi manual lewat form.

Jalankan php artisan migrate:fresh --seed kapan saja ingin kembali ke kondisi database yang bersih. Ini sangat berguna sebelum menguji fitur baru atau saat mengikuti tutorial yang membutuhkan data awal yang konsisten.

Verifikasi Data

Pastikan data benar-benar sudah masuk menggunakan Artisan tinker:

php artisan tinker
>>> App\Models\Catatan::count()
=> 28

>>> App\Models\Catatan::latest()->first()->judul
=> "Tadabbur Surat Al-Alaq Tentang Ilmu"

28 catatan dengan konten yang bervariasi — data sudah siap untuk diuji di semua fitur yang sudah dibangun sebelumnya, termasuk fitur berikutnya.

Dengan database yang selalu bisa direset ke kondisi awal yang konsisten, pengujian fitur berikutnya menjadi jauh lebih terkontrol. Dan fitur yang perlu diuji selanjutnya adalah satu yang langsung terasa manfaatnya dengan 28 data ini: halaman listing yang saat ini menampilkan semua catatan sekaligus perlu dipecah menjadi beberapa halaman agar lebih nyaman dinavigasi.

Referensi

  1. 1Database: Seeding — Laravel 12.x Documentation
  2. 2Eloquent: Factories — Laravel 12.x Documentation
  3. 3Faker PHP — The PHP Fake Data Generator