reedware/laravel-modern-factories
Bring Laravel 8-style class-based model factories to legacy Laravel (5.1–7.x) apps. Write modern PHPUnit-friendly factories today to reduce upgrade pain later, without facades or service providers. Supports PHP 5.5–8.4.
Installation:
composer require reedware/laravel-modern-factories
No service provider or facade registration is required.
Define a Factory:
Create a factory class in database/factories (or your preferred location) following Laravel 8.x syntax:
// database/factories/UserFactory.php
namespace Database\Factories;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
class UserFactory extends Factory
{
protected $model = User::class;
public function definition()
{
return [
'name' => $this->faker->name,
'email' => $this->faker->unique()->safeEmail,
];
}
}
First Use Case: Use the factory in tests or seeders:
use App\Models\User;
use Database\Factories\UserFactory;
$user = UserFactory::new()->create();
Stateful Factories: Define custom states for reusable variations:
public function admin()
{
return $this->state([
'is_admin' => true,
]);
}
Usage:
$admin = UserFactory::new()->admin()->create();
AfterCreating/AfterMaking: Hook into factory lifecycle:
protected static function newModel()
{
return User::new();
}
public function afterCreating(Model $model)
{
$model->update(['verified_at' => now()]);
}
Relationships: Define relationships between factories:
public function configure()
{
return $this->afterCreating(function (User $user) {
$user->posts()->save(PostFactory::new());
});
}
Test Data Setup:
Use factories in DatabaseSeeder or test setups:
public function run()
{
User::factory()->count(10)->create();
}
Mocking APIs:
Combine with Faker for realistic test data:
$user = UserFactory::new()->create([
'email' => 'test@example.com',
'password' => bcrypt('password'),
]);
Legacy Migration Testing: Test database migrations with factory-generated data:
public function test_migration()
{
User::factory()->create();
$this->artisan('migrate:fresh')->assertExitCode(0);
}
new/for with newFactory/forModel in factory definitions.Faker\Generator for domain-specific data:
$factory->afterMaking(function (User $user) {
$user->apiToken = Str::random(60);
});
Namespace Conflicts:
Ensure factories extend Illuminate\Database\Eloquent\Factories\Factory (not Illuminate\Database\Eloquent\ModelFactory).
Fix: Explicitly use the modern namespace:
use Illuminate\Database\Eloquent\Factories\Factory;
PHP Version Restrictions:
new/for keywords; use newFactory/forModel.afterMaking) may not work.
Fix: Check versioning table.Legacy Eloquent Quirks:
has()/for() methods may behave differently in older Laravel.
Fix: Test thoroughly and use afterCreating as a fallback.Autoloading Issues:
If factories aren’t autoloaded, manually register the namespace in composer.json:
"autoload": {
"psr-4": {
"Database\\Factories\\": "database/factories"
}
}
Factory Not Found: Verify the factory class exists and is autoloaded. Run:
composer dump-autoload
Method Not Allowed: For PHP < 7.0, replace:
$factory->for(User::class); // Error
With:
$factory->forModel(User::class); // Works
Faker Issues:
Ensure fakerphp/faker is installed and compatible with your PHP version.
Partial Upgrades: Use the package to modernize factories gradually while keeping the rest of the app on an older Laravel version.
Testing Compatibility: Validate factories work with your Laravel version by running:
php artisan tinker
User::factory()->create();
Performance:
For bulk operations, use create() instead of make() to avoid N+1 queries:
User::factory()->count(100)->create(); // Efficient
Extending Factories: Create a base factory for shared logic:
class BaseFactory extends Factory
{
public function withTimestamps()
{
return $this->state([
'created_at' => now(),
'updated_at' => now(),
]);
}
}
Documentation: Add factory usage examples to your test files for future developers:
// Example: UserFactory::new()->admin()->withPosts(3)->create();
CI/CD Integration: Use factories in CI pipelines to ensure test data consistency:
# .github/workflows/tests.yml
jobs:
test:
runs-on: ubuntu-latest
steps:
- run: php artisan db:seed --class=TestDatabaseSeeder
How can I help you explore Laravel packages today?