Skip to content

Factories

Overview

Factories generate fake model instances for testing and development. Sutomo uses Laravel's Eloquent Factory pattern, organized by domain. Each factory defines sensible defaults using Faker — making it easy to create realistic test data with minimal boilerplate.

Factories are used in:

  • Tests — via Model::factory()->create() or Model::factory()->make()
  • Dummy seedersDummySeed.php calls factories to populate development environments
  • Feature demos — Quick data generation for UI previews

Folder Structure

database/factories/
├── UserFactory.php                     # Top-level user factory
├── ContactFactory.php                  # Contact factory
├── Academic/                           # Academic domain
│   ├── AnnouncementFactory.php
│   └── ...
├── Administration/                     # Administration
│   └── ...
├── Educator/                           # Educator domain
│   └── TeacherFactory.php
├── Geographic/                         # Geographic domain
│   └── ...
├── Institution/                        # Institution domain
│   └── SchoolFactory.php
├── LeaveManagement/                    # Leave management
│   └── ...
├── Platform/                           # Platform factories
│   ├── Approval/
│   ├── Bulletin/
│   ├── ContactMessage/
│   ├── DocumentType/
│   ├── Employee/
│   ├── Gender/
│   ├── Onboarding/
│   ├── Pipeline/
│   ├── Rating/
│   ├── Stage/
│   └── Template/
├── Recruitment/                        # Hiring/Recruitment
│   ├── ApplicantFactory.php
│   ├── CandidateEducationFactory.php
│   ├── CandidateExperienceFactory.php
│   ├── CandidateFactory.php
│   ├── VacancyFactory.php
│   └── VacancyStateFactory.php
└── Support/
    └── ...

Patterns

1. Standard Factory

Each factory maps to a single model and defines default attribute values:

php
class SchoolFactory extends Factory
{
    protected $model = School::class;

    public function definition(): array
    {
        return [
            'code' => $this->faker->unique()->word(),
            'name' => $this->faker->company(),
            'country_code' => $this->faker->countryCode(),
            'address' => $this->faker->address(),
        ];
    }
}

2. UserFactory with Shared Password

The UserFactory stores a shared password hash so all generated users use the same password — useful for testing login:

php
class UserFactory extends Factory
{
    protected static ?string $password;

    public function definition(): array
    {
        return [
            'name' => fake()->name(),
            'email' => fake()->unique()->safeEmail(),
            'password' => static::$password ??= Hash::make('password'),
            // ...
        ];
    }

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

3. Custom States

Use factory states for variations:

php
// Without calling unverified(), user is verified
$user = User::factory()->create();

// Unverified user for email verification tests
$user = User::factory()->unverified()->create();

4. Domain Organization

Factories are nested by domain matching the model namespace. For example, CandidateFactory.php lives in database/factories/Recruitment/ matching App\Models\Recruitment\Candidate\Candidate:

php
namespace Database\Factories\Recruitment;

use App\Models\Recruitment\Candidate\Candidate;

class CandidateFactory extends Factory
{
    protected $model = Candidate::class;
    // ...
}

5. Using Factories in Seeders

php
class DummySeed extends Seeder
{
    public function run(): void
    {
        // Create 1 school with 10 teachers and 100 students
        $school = School::factory()->create();
        Teacher::factory()->count(10)->create(['school_id' => $school->id]);
        Student::factory()->count(100)->create(['school_id' => $school->id]);
    }
}

Key Files

database/factories/
├── UserFactory.php                              # User model — shared password, unverified state
├── ContactFactory.php                           # Contact model
├── Institution/SchoolFactory.php                # School with default location data
├── Recruitment/
│   ├── CandidateFactory.php                     # Job candidate
│   ├── CandidateEducationFactory.php            # Candidate education history
│   ├── CandidateExperienceFactory.php           # Candidate work experience
│   ├── ApplicantFactory.php                     # Job applicant
│   ├── VacancyFactory.php                       # Job vacancy
│   └── VacancyStateFactory.php                  # Vacancy workflow state
├── Platform/
│   ├── Gender/GenderFactory.php                 # Gender reference
│   ├── Employee/EmployeeFactory.php             # Employee records
│   ├── Approval/ApprovalFactory.php             # Approval workflows
│   ├── Pipeline/PipelineFactory.php             # Hiring/contract pipelines
│   ├── Stage/StageFactory.php                   # Pipeline stages
│   ├── Template/TemplateFactory.php             # Message templates
│   ├── Rating/RatingFactory.php                 # Platform ratings
│   ├── Bulletin/BulletinFactory.php             # Bulletins
│   ├── Onboarding/OnboardingFactory.php         # Onboarding items
│   └── DocumentType/DocumentTypeFactory.php     # Document types
├── Educator/
│   └── TeacherFactory.php                       # Teacher records
├── Academic/
│   └── AnnouncementFactory.php                  # Academic announcements
└── LeaveManagement/
    └── ...