โ† Semua pembelajaran / Laravel Nol โ†’ Enterprise
Fase 2 ยท Database & Eloquent

Factory & seeder

Tes yang bergantung pada data manual akan rapuh dan lambat ditulis. Factory membuat data yang valid secara default, sehingga setiap tes hanya perlu menyebutkan bagian yang benar-benar penting baginya.

Sumber asli laravel.com Resmi Rangkuman ~6 menit baca

Intisari

  • Factory menyediakan nilai default yang valid; tes hanya menimpa field yang relevan bagi dirinya.
  • State memberi nama pada variasi: Produk::factory()->habis()->create().
  • Relasi dibuat sekaligus: ->has(Ulasan::factory()->count(3)) atau ->for($kategori).
  • make() hanya membuat objek; create() menyimpannya ke database.
  • Seeder untuk data referensi (kategori, peran); factory untuk data uji. Jangan tertukar.

Bentuk sebuah factory

php artisan make:factory ProdukFactory --model=Produk
class ProdukFactory extends Factory
{
    protected $model = Produk::class;

    public function definition(): array
    {
        $nama = fake()->words(3, true);

        return [
            'kategori_id' => Kategori::factory(),        // dibuat otomatis kalau belum ada
            'nama'        => $nama,
            'slug'        => Str::slug($nama).'-'.fake()->unique()->numberBetween(1, 99999),
            'harga'       => fake()->numberBetween(5_000, 500_000),
            'stok'        => fake()->numberBetween(0, 100),
            'status'      => StatusProduk::Aktif,
        ];
    }

    // State: variasi yang punya nama
    public function habis(): static
    {
        return $this->state(fn () => ['stok' => 0]);
    }

    public function mahal(): static
    {
        return $this->state(fn () => ['harga' => fake()->numberBetween(1_000_000, 5_000_000)]);
    }
}

Memakainya

Produk::factory()->create();                        // satu, tersimpan
Produk::factory()->count(50)->create();             // lima puluh
Produk::factory()->make();                          // tidak menyentuh database

Produk::factory()->habis()->mahal()->create();      // state bisa digabung
Produk::factory()->create(['nama' => 'Kopi Susu']); // timpa field tertentu

// Dengan relasi
Produk::factory()
    ->for(Kategori::factory()->create(['nama' => 'Minuman']))
    ->has(Ulasan::factory()->count(3))
    ->create();

// Urutan yang berputar โ€” berguna untuk memastikan variasi
Produk::factory()
    ->count(9)
    ->sequence(
        ['status' => StatusProduk::Aktif],
        ['status' => StatusProduk::Arsip],
        ['status' => StatusProduk::Draf],
    )
    ->create();

Ini yang membuat tes terbaca. Bandingkan dua tes: satu yang mengisi dua belas kolom hanya untuk memeriksa perilaku saat stok nol, dan satu yang menulis Produk::factory()->habis()->create(). Yang kedua menyatakan maksudnya; yang pertama menyembunyikannya di antara nilai-nilai yang tidak relevan.

Nilai unik dan jebakannya

fake()->unique()->safeEmail();
fake()->unique()->numberBetween(1, 100);   // akan kehabisan setelah 100 pemanggilan

unique() mengingat nilai yang sudah dipakai dalam satu proses. Kalau ruang nilainya sempit, factory ke-101 akan melempar OverflowException. Untuk slug dan kode, gabungkan dengan sesuatu yang benar-benar unik seperti angka acak besar atau Str::ulid().

Seeder: data yang memang harus ada

class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        // Data referensi: nyata, tetap, dan dibutuhkan aplikasi untuk berjalan
        foreach (['Minuman', 'Makanan', 'Peralatan'] as $nama) {
            Kategori::firstOrCreate(['slug' => Str::slug($nama)], ['nama' => $nama]);
        }

        // Data uji: hanya di luar produksi
        if (! app()->isProduction()) {
            User::factory()->create([
                'email'    => '[email protected]',
                'password' => 'password',
            ]);

            Produk::factory()->count(100)->create();
        }
    }
}
php artisan db:seed
php artisan db:seed --class=KategoriSeeder
php artisan migrate:fresh --seed          # aman di lokal, TIDAK PERNAH di produksi
SeederFactory
IsinyaData referensi yang nyataData palsu yang bentuknya benar
Boleh jalan di produksiYa, bagian yang idempotenTidak pernah
Dipanggil daridb:seed, deploy pertamaTes dan seeder
Harus idempotenYa โ€” firstOrCreateTidak relevan

Seeder produksi wajib idempoten. Ia akan dijalankan lagi โ€” saat deploy ulang, saat memulihkan lingkungan, saat seseorang menjalankannya karena ragu. Kategori::create(...) akan menghasilkan duplikat pada pemanggilan kedua; firstOrCreate(...) tidak.

Bahasa Indonesia di data palsu

// config/app.php
'faker_locale' => 'id_ID',

Dengan locale id_ID, nama, alamat, dan nomor telepon yang dihasilkan berbentuk Indonesia. Ini bukan kosmetik: layar yang penuh nama Indonesia akan mengungkap masalah lebar kolom dan pemotongan teks yang tidak terlihat kalau semua nama contoh berbunyi "John Doe".

Latihan: buat ProdukFactory dengan state habis(), lalu jalankan php artisan tinker dan buat 20 produk yang tiga di antaranya habis. Verifikasi dengan Produk::where('stok', 0)->count(). Data ini akan kamu pakai lagi di Fase 6.

Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.