Skip to content

Seeders

Overview

Seeders populate the database with initial or reference data required for the application to function. In Sutomo, seeders are organized by domain matching the model structure — each domain has its own seeder directory with individual seeders for specific entities, coordinated by a RunnerSeeder.

Why Use Seeders?

  • Essential reference data — Genders, Religions, Ethnicities, Localizations must exist before any user can set their profile
  • Demo/development data — Dummy schools, classes, students, and hiring pipelines for testing
  • Setup automation — The setup wizard uses seeders to populate geographic data, settings, and master data
  • Consistency — Ensures all environments (local, staging, production) have the same baseline data

Folder Structure

database/seeders/
├── DatabaseSeeder.php              # Top-level orchestrator
├── Academic/                       # Academic domain seeders
│   ├── RunnerSeeder.php
│   ├── DummySeed.php
│   ├── EnrollmentSeeder.php
│   ├── QuizSeeder.php
│   ├── SubjectSeeder.php
│   └── TranscriptSeeder.php
├── Acquisition/
│   └── RunnerSeeder.php
├── Assistant/
│   ├── RunnerSeeder.php
│   └── ChatbotTrainingSeeder.php
├── Bulletin/
│   └── AnnouncementSeeder.php
├── ContractManagement/
│   └── RunnerSeeder.php
├── Core/                           # Core platform data
│   ├── RunnerSeeder.php
│   ├── BannerSeeder.php
│   ├── DepositTypeSeeder.php
│   ├── DiscountSeeder.php
│   ├── DocumentTypeSeeder.php
│   ├── EmployeeSeeder.php
│   ├── EthnicitySeeder.php
│   ├── GenderSeeder.php
│   ├── LocalizationSeeder.php
│   ├── ReligionSeeder.php
│   ├── RoleSeeder.php
│   ├── SettingSeeder.php
│   ├── ShieldSeeder.php
│   └── UserSeeder.php
├── Financial/
│   ├── RunnerSeeder.php
│   ├── FinanceSettingSeeder.php
│   ├── PaymentMethodSeeder.php
│   └── TransactionSeeder.php
├── Geographic/
│   └── RunnerSeeder.php
├── Hiring/
│   ├── RunnerSeeder.php
│   ├── ApplicantSeeder.php
│   ├── CandidateEducationSeeder.php
│   ├── CandidateExperienceSeeder.php
│   ├── CandidateSeeder.php
│   ├── HiringPipelineSeeder.php
│   ├── RequireTestSeeder.php
│   ├── TalentInterviewSeeder.php
│   └── VacancySeeder.php
├── Institution/
│   └── RunnerSeeder.php
├── News/
│   └── RunnerSeeder.php
├── Organization/
│   └── RunnerSeeder.php
├── Platform/
│   ├── AboutPageSeeder.php
│   ├── BulletinSeeder.php
│   ├── FacilitySeeder.php
│   ├── OnboardingSeeder.php
│   ├── PropertySeeder.php
│   └── RatingSeeder.php
└── Support/
    └── RunnerSeeder.php

Patterns

1. RunnerSeeder Pattern

Each domain has a RunnerSeeder that calls individual seeders in the correct order. This keeps DatabaseSeeder.php clean and allows domains to manage their own dependencies.

php
// database/seeders/Core/RunnerSeeder.php
class RunnerSeeder extends Seeder
{
    public function run(): void
    {
        $this->call([
            GenderSeeder::class,       // no dependencies
            ReligionSeeder::class,     // no dependencies
            EthnicitySeeder::class,    // no dependencies
            RoleSeeder::class,         // must run before UserSeeder & EmployeeSeeder
            UserSeeder::class,
            EmployeeSeeder::class,
            SettingSeeder::class,
            // ...
        ]);
    }
}

2. updateOrCreate — Idempotent Seeders

Reference data uses updateOrCreate so seeders can be run multiple times without creating duplicates:

php
class GenderSeeder extends Seeder
{
    public function run(): void
    {
        foreach ($this->data() as $item) {
            Gender::query()->updateOrCreate(
                ['code' => $item['code']],          // unique match
                ['name' => $item['name']],           // update or set
            );
        }
    }

    private function data(): array
    {
        return [
            ['code' => 'male',   'name' => ['en' => 'Male',   'id' => 'Laki-laki']],
            ['code' => 'female', 'name' => ['en' => 'Female', 'id' => 'Perempuan']],
        ];
    }
}

3. ShieldSeeder — Role & Permission Setup

The ShieldSeeder ensures standard roles and custom permissions exist. It uses firstOrCreate to safely run alongside Filament Shield's own role generation.

php
class ShieldSeeder extends Seeder
{
    public function run(): void
    {
        $superUserRole = Role::firstOrCreate([
            'name' => 'super_user', 'guard_name' => 'web',
        ]);

        $importPerm = Permission::firstOrCreate([
            'name' => 'import_data', 'guard_name' => 'web',
        ]);

        $superUserRole->givePermissionTo($importPerm);
    }
}

4. DummySeed — Development Data

Dummy seeders use factories to generate realistic test data for development and staging environments.

php
class DummySeed extends Seeder
{
    public function run(): void
    {
        School::factory()->count(3)->create();
        Teacher::factory()->count(20)->create();
        Student::factory()->count(200)->create();
    }
}

Key Files

database/seeders/
├── DatabaseSeeder.php                   # Top-level seeder — calls all RunnerSeeders
├── Core/
│   ├── RunnerSeeder.php                 # Core domain orchestrator
│   ├── GenderSeeder.php                 # Gender reference data
│   ├── ReligionSeeder.php               # Religion reference data
│   ├── EthnicitySeeder.php              # Ethnicity reference data
│   ├── RoleSeeder.php                   # Spatie roles
│   ├── ShieldSeeder.php                 # Custom permissions
│   ├── UserSeeder.php                   # Default admin users
│   ├── SettingSeeder.php                # Default system settings
│   └── LocalizationSeeder.php           # Available locales
├── Geographic/
│   └── RunnerSeeder.php                 # Countries, states, cities
├── Institution/
│   └── RunnerSeeder.php                 # Schools, education levels
├── Organization/
│   └── RunnerSeeder.php                 # Departments, positions
├── Hiring/
│   └── RunnerSeeder.php                 # Hiring pipelines, candidates
├── Academic/
│   ├── RunnerSeeder.php                 # Academic data
│   ├── DummySeed.php                    # Development dummy data
│   └── ...
└── ...