The HasDataFactory trait allows you to integrate factory functionality directly into your classes, providing a clean and intuitive API.
This follows the same pattern as Laravel's Eloquent factories (User::factory()), but works with any PHP class—not just Eloquent models.
Use the HasDataFactory trait on your class and implement the newFactory() method:
<?php
use FBarrento\DataFactory\Concerns\HasDataFactory;
class Application
{
use HasDataFactory;
public function __construct(
public string $id,
public string $name,
public string $slug,
public string $region,
) {}
protected static function newFactory(): ApplicationFactory
{
return new ApplicationFactory();
}
}Once the trait is added, you can call factory() directly on your class:
// Create a single instance
$application = Application::factory()->make();
// Create multiple instances
$applications = Application::factory()->count(5)->make();
// Use states
$production = Application::factory()->production()->make();
// Override attributes
$custom = Application::factory()->make([
'name' => 'My Application',
'region' => 'eu-west-1',
]);<?php
use FBarrento\DataFactory\Factory;
use FBarrento\DataFactory\Concerns\HasDataFactory;
// Model class
class Deployment
{
use HasDataFactory;
public function __construct(
public string $id,
public string $status,
public string $branchName,
public string $commitHash,
public ?string $commitMessage = null,
) {}
protected static function newFactory(): DeploymentFactory
{
return new DeploymentFactory();
}
}
// Factory class
class DeploymentFactory extends Factory
{
protected function definition(): array
{
return [
'id' => $this->fake->uuid(),
'status' => 'pending',
'branchName' => 'main',
'commitHash' => $this->fake->sha1(),
'commitMessage' => $this->fake->sentence(),
];
}
public function succeeded(): static
{
return $this->state([
'status' => 'deployment.succeeded',
]);
}
}
// Usage
$deployment = Deployment::factory()->make();
$succeeded = Deployment::factory()->succeeded()->make();
$multiple = Deployment::factory()->count(10)->make();// With trait
$app = Application::factory()->make();
// Without trait (not recommended for objects)
$app = ApplicationFactory::new()->make();Your IDE can autocomplete Application::factory() when you have the class open.
The API intentionally mirrors Laravel's Eloquent factories for consistency:
// Laravel Eloquent (creates and saves to database)
$user = User::factory()->create();
// Data Factory (creates object in memory)
$user = User::factory()->make();If you already know Laravel's factory pattern, you already know Data Factory—the difference is make() instead of create() since we're not persisting to a database.
<?php
use FBarrento\DataFactory\Concerns\HasDataFactory;
class Environment
{
use HasDataFactory;
public function __construct(
public string $id,
public string $name,
public string $slug,
public string $status,
public string $phpMajorVersion,
public bool $usesOctane,
) {}
protected static function newFactory(): EnvironmentFactory
{
return new EnvironmentFactory();
}
}
class EnvironmentFactory extends Factory
{
protected function definition(): array
{
return [
'id' => $this->fake->uuid(),
'name' => $this->fake->word(),
'slug' => $this->fake->slug(),
'status' => 'running',
'phpMajorVersion' => '8.4',
'usesOctane' => false,
];
}
public function production(): static
{
return $this->state([
'name' => 'production',
'slug' => 'production',
'usesOctane' => true,
]);
}
public function staging(): static
{
return $this->state([
'name' => 'staging',
'slug' => 'staging',
]);
}
}
// Clean usage in tests
$production = Environment::factory()->production()->make();
$staging = Environment::factory()->staging()->make();
$environments = Environment::factory()->count(5)->make();<?php
// Organization Class
class Organization
{
use HasDataFactory;
public function __construct(
public string $id,
public string $name,
) {}
protected static function newFactory(): OrganizationFactory
{
return new OrganizationFactory();
}
}
class OrganizationFactory extends Factory
{
protected function definition(): array
{
return [
'id' => $this->fake->uuid(),
'name' => $this->fake->company(),
];
}
}
// Repository Class
class Repository
{
use HasDataFactory;
public function __construct(
public string $id,
public string $name,
public string $fullName,
public string $defaultBranch,
) {}
protected static function newFactory(): RepositoryFactory
{
return new RepositoryFactory();
}
}
class RepositoryFactory extends Factory
{
protected function definition(): array
{
return [
'id' => $this->fake->uuid(),
'name' => $this->fake->slug(),
'fullName' => $this->fake->userName() . '/' . $this->fake->slug(),
'defaultBranch' => 'main',
];
}
}
// Application Class with nested factories
class Application
{
use HasDataFactory;
public function __construct(
public string $id,
public string $name,
public Repository $repository,
public Organization $organization,
) {}
protected static function newFactory(): ApplicationFactory
{
return new ApplicationFactory();
}
}
class ApplicationFactory extends Factory
{
protected function definition(): array
{
return [
'id' => $this->fake->uuid(),
'name' => $this->fake->company(),
'repository' => Repository::factory(), // Uses the trait!
'organization' => Organization::factory(), // Uses the trait!
];
}
}
// Usage
$application = Application::factory()->make();
// $application->repository and $application->organization are automatically created- You have dedicated classes (domain models, DTOs, value objects)
- You want clean, consistent syntax
- You're building a domain model
- You want IDE autocompletion on the class
- You're only creating arrays (use
ArrayFactorydirectly) - You don't have dedicated classes
- You prefer explicit factory instantiation
- You're creating ad-hoc test data
A common pattern is to organize your code like this:
src/
├── Domain/
│ ├── Application.php
│ ├── Environment.php
│ └── Deployment.php
└── Factories/
├── ApplicationFactory.php
├── EnvironmentFactory.php
└── DeploymentFactory.php
Or co-locate factories with classes:
src/
├── Application/
│ ├── Application.php
│ └── ApplicationFactory.php
├── Environment/
│ ├── Environment.php
│ └── EnvironmentFactory.php
└── Deployment/
├── Deployment.php
└── DeploymentFactory.php
The trait maintains full type safety:
class Application
{
use HasDataFactory;
// ... constructor
protected static function newFactory(): ApplicationFactory // Return type enforced
{
return new ApplicationFactory();
}
}Your IDE and static analysis tools (like PHPStan) will know that Application::factory() returns an ApplicationFactory instance.
- Advanced Examples - Complete real-world examples
- Testing - Use factories with the trait in PEST tests
- Nested Factories - Combine trait usage with nested factories