BAB 22: Seeding dan Factory

Mengisi database dengan data awal menggunakan seeder dan factory di Laravel.

Saat membangun aplikasi, kamu perlu data untuk diuji. Membuat data manual lewat form setiap kali menjalankan migrate:fresh adalah pemborosan waktu yang nyata. Ada cara yang jauh lebih baik: seeder dan factory.

Seeder adalah class PHP yang bertugas mengisi database dengan data awal. Factory adalah blueprint untuk membuat instance model dengan data yang di-generate otomatis — termasuk data palsu yang realistis seperti nama, email, dan paragraf teks menggunakan library Faker. Kombinasi keduanya memungkinkan kamu mengisi database dengan puluhan atau ratusan record yang masuk akal hanya dalam satu perintah.

Membuat Seeder

Buat seeder menggunakan Artisan:

php artisan make:seeder CatatanSeeder

File baru muncul di database/seeders/CatatanSeeder.php:

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

namespace Database\Seeders;

use Illuminate\Database\Seeder;

class CatatanSeeder extends Seeder
{
    public function run(): void
    {
        // logika pengisian data di sini
    }
}

Method run() adalah satu-satunya tempat kamu menulis logika seeder. Di dalamnya bisa menggunakan Query Builder, Eloquent, atau factory.

Untuk sementara, isi dengan data statis menggunakan Eloquent:

// database/seeders/CatatanSeeder.php
use App\Models\Catatan;
use App\Models\User;

public function run(): void
{
    $user = User::first();

    Catatan::create([
        'user_id'   => $user->id,
        'judul'     => 'Persiapan sprint minggu ini',
        'isi'       => 'Review backlog, estimasi story points, dan assign task ke tim.',
        'prioritas' => 'tinggi',
        'selesai'   => false,
    ]);

    Catatan::create([
        'user_id'   => $user->id,
        'judul'     => 'Baca dokumentasi Eloquent',
        'isi'       => 'Fokus pada bagian relationships dan eager loading.',
        'prioritas' => 'normal',
        'selesai'   => true,
    ]);
}

DatabaseSeeder sebagai Orkestrator

File database/seeders/DatabaseSeeder.php adalah seeder induk yang Laravel jalankan secara default. Daftarkan seeder lain di sini menggunakan $this->call():

// 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 — seeder yang bergantung pada data dari seeder lain harus diletakkan setelahnya.

Menjalankan Seeder

Beberapa perintah yang perlu diketahui:

# Jalankan DatabaseSeeder (default)
php artisan db:seed

# Jalankan seeder tertentu saja
php artisan db:seed --class=CatatanSeeder

# Reset database, jalankan semua migrasi, lalu seed
php artisan migrate:fresh --seed

Perintah migrate:fresh --seed adalah yang paling sering dipakai selama pengembangan — ia menghapus semua tabel, membuat ulang dari awal, lalu mengisi data. Sangat berguna setelah mengubah struktur migrasi.

migrate:fresh menghapus semua tabel dan data yang ada. Jangan jalankan di database production. Gunakan --force hanya jika benar-benar diperlukan di environment non-development.

Model Factory

Seeder dengan data statis seperti contoh di atas cukup untuk beberapa record, tapi tidak praktis ketika kamu butuh 50 atau 100 record dengan variasi data yang realistis. Di sinilah factory berperan.

Buat factory untuk model Catatan:

php artisan make:factory CatatanFactory --model=Catatan

File baru muncul di database/factories/CatatanFactory.php. Isi method definition() dengan blueprint atribut menggunakan helper fake():

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

namespace Database\Factories;

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

class CatatanFactory extends Factory
{
    public function definition(): array
    {
        return [
            'user_id'   => User::factory(),
            'judul'     => fake()->sentence(nb: 5, variableNbWords: true),
            'isi'       => fake()->paragraphs(nb: 2, asText: true),
            'prioritas' => fake()->randomElement(['rendah', 'normal', 'tinggi']),
            'selesai'   => fake()->boolean(chanceOfGettingTrue: 30),
        ];
    }
}

Beberapa hal yang perlu diperhatikan di sini:

  • User::factory() sebagai nilai user_id artinya setiap kali factory Catatan digunakan, ia akan otomatis membuat User baru jika tidak ada yang di-supply. Ini memudahkan pembuatan data yang berelasi.
  • fake()->sentence() menghasilkan kalimat acak sebagai judul.
  • fake()->boolean(30) menghasilkan true 30% dari waktu — sehingga mayoritas catatan berstatus belum selesai, yang lebih realistis.

Tambahkan trait HasFactory ke model Catatan agar factory bisa digunakan:

// app/Models/Catatan.php
use Illuminate\Database\Eloquent\Factories\HasFactory;

class Catatan extends Model
{
    use HasFactory;
    use SoftDeletes;

    // ...
}

Membuat Record dengan Factory

// Buat 1 record, simpan ke database
$catatan = Catatan::factory()->create();

// Buat 10 record sekaligus
$catatans = Catatan::factory()->count(10)->create();

// Override atribut tertentu
$catatan = Catatan::factory()->create([
    'prioritas' => 'mendesak',
    'selesai'   => false,
]);

// Buat instance tanpa menyimpan ke database (untuk unit test)
$catatan = Catatan::factory()->make();

Factory States

Untuk variasi yang sering dipakai, definisikan state di factory:

// database/factories/CatatanFactory.php
public function mendesak(): static
{
    return $this->state(fn (array $attributes) => [
        'prioritas' => 'mendesak',
        'selesai'   => false,
    ]);
}

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

Gunakan state saat membuat data:

// 5 catatan mendesak
Catatan::factory()->count(5)->mendesak()->create();

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

// Bisa di-chain
Catatan::factory()->sudahSelesai()->create(['prioritas' => 'rendah']);

Factory dengan Sequence

Untuk membuat data dengan pola berulang, gunakan sequence():

// Buat 6 catatan dengan prioritas bergantian
Catatan::factory()
    ->count(6)
    ->sequence(
        ['prioritas' => 'rendah'],
        ['prioritas' => 'normal'],
        ['prioritas' => 'tinggi'],
    )
    ->create();

Hasilnya: 2 catatan rendah, 2 normal, 2 tinggi — sequence otomatis berulang.

Menggunakan Factory di Seeder

Perbarui CatatanSeeder untuk menggunakan factory alih-alih data statis:

// database/seeders/CatatanSeeder.php
use App\Models\Catatan;
use App\Models\User;

public function run(): void
{
    // Pastikan ada minimal 1 user
    $users = User::factory()->count(3)->create();

    // Buat 20 catatan yang tersebar ke 3 user
    $users->each(function (User $user) {
        Catatan::factory()
            ->count(fake()->numberBetween(3, 8))
            ->for($user)
            ->create();
    });

    // Tambahkan beberapa catatan mendesak
    Catatan::factory()
        ->count(5)
        ->mendesak()
        ->for($users->first())
        ->create();
}

Method for() digunakan untuk mengikat catatan ke user tertentu, menggantikan user_id yang ada di factory. Hasilnya: setiap user punya 3–8 catatan biasa ditambah 5 catatan mendesak untuk user pertama.

Jalankan:

php artisan migrate:fresh --seed

Database langsung terisi dengan data yang bervariasi dan realistis.

Tambahkan fake()->locale('id_ID') di config/app.php dengan key faker_locale untuk mendapatkan nama dan alamat Indonesia dari Faker, bukan nama barat. Ini membuat data seed terasa lebih relevan saat diperagakan ke klien lokal.

Latihan

Coba kerjakan latihan berikut:

  1. Factory label — Buat factory untuk model Label yang kita buat di BAB 20. Gunakan fake()->colorName() untuk nama label dan fake()->hexColor() untuk warna. Buat LabelSeeder yang menghasilkan 10 label, lalu daftarkan di DatabaseSeeder.

  2. Relasi many-to-many di seeder — Di CatatanSeeder, setelah membuat catatan, gunakan sync() untuk melampirkan 1–3 label acak ke setiap catatan. Hint: Label::inRandomOrder()->limit(rand(1, 3))->pluck('id').

  3. State kustom — Tambahkan state terlambat() ke CatatanFactory yang mengisi kolom tenggat dengan tanggal di masa lalu dan selesai bernilai false. Gunakan state ini untuk membuat 5 catatan terlambat di seeder.

Penutup Bab

Seeder dan factory menyelesaikan persiapan layer database kita. Model sudah punya relasi, atribut sudah punya transformasi yang tepat, dan sekarang database sudah bisa diisi dengan data yang realistis kapan saja dibutuhkan.

Bagian berikutnya membawa semua fondasi yang sudah dibangun ke dalam konteks nyata: membangun aplikasi CRUD lengkap dari awal. Kita akan mulai dari menginstal proyek Laravel baru, menghubungkannya ke database, lalu membangun fitur satu per satu sampai aplikasi benar-benar bisa digunakan.

Referensi

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