Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Foundry Laravel Package

zenstruck/foundry

Foundry makes fixture creation enjoyable with an expressive, auto-completable, on-demand factory system for Symfony and Doctrine. Create and persist objects with minimal data, use factories in DoctrineFixturesBundle or tests, and support ORM, MongoDB ODM, or both.

View on GitHub
Deep Wiki
Context7
## Getting Started

### Minimal Setup
1. **Installation**:
   ```bash
   composer require zenstruck/foundry --dev

For Symfony projects, also install the bundle:

composer require zenstruck/foundry-bundle --dev
  1. Bootstrapping: Add the FoundryServiceProvider to your config/app.php (Symfony) or run:

    php artisan foundry:install
    

    This generates a config/foundry.php with default settings.

  2. First Factory: Create a factory for a model (e.g., User) using the Artisan command:

    php artisan make:factory UserFactory --model=User
    

    This generates a factory class in database/factories/UserFactory.php:

    use Zenstruck\Foundry\ModelFactory;
    use Zenstruck\Foundry\Proxy;
    
    final class UserFactory extends ModelFactory
    {
        protected function configure(): void
        {
            $this->sequence(['name' => 'User {n}']);
        }
    
        protected function modelBuilder(): ModelBuilder
        {
            return User::newModelInstance();
        }
    
        protected static function class(): string
        {
            return User::class;
        }
    }
    
  3. First Usage: Use the factory in tests or fixtures:

    $user = UserFactory::new()->create();
    

    Or with custom attributes:

    $user = UserFactory::new()->create(['name' => 'John Doe', 'email' => '[email protected]']);
    

First Use Case: Test Fixtures

Replace hardcoded test data with dynamic factories:

public function test_user_can_login(): void
{
    $user = UserFactory::new()->create(['password' => 'password123']);
    $this->actingAs($user);

    $response = $this->get('/dashboard');
    $response->assertOk();
}

Implementation Patterns

1. Factory Methods

Define reusable factory methods for common states:

final class PostFactory extends ModelFactory
{
    public function published(): static
    {
        return $this->state([
            'published_at' => now(),
            'status' => 'published',
        ]);
    }

    public function draft(): static
    {
        return $this->state(['status' => 'draft']);
    }
}

Usage:

$post = PostFactory::new()->published()->create();

2. Relationships

Define relationships between factories:

final class CommentFactory extends ModelFactory
{
    protected function modelBuilder(): ModelBuilder
    {
        return Comment::newModelInstance(['post' => PostFactory::random()]);
    }
}

Or use has()/hasMany():

final class PostFactory extends ModelFactory
{
    public function withComments(int $count = 3): static
    {
        return $this->has(CommentFactory::new(), $count);
    }
}

Usage:

$post = PostFactory::new()->withComments(5)->create();

3. State Management

Override states dynamically:

$post = PostFactory::new()
    ->state(function (array $attributes, PostFactory $factory) {
        return ['slug' => Str::slug($attributes['title'])];
    })
    ->create();

4. Testing Workflows

Unit Tests

Use Foundry::cleanup() to reset the database after each test:

use Zenstruck\Foundry\Test\Factories;
use Zenstruck\Foundry\Test\ResetDatabase;

#[ResetDatabase]
class UserTest extends TestCase
{
    use Factories;

    public function test_user_creation(): void
    {
        $user = UserFactory::new()->create();
        $this->assertDatabaseHas('users', ['id' => $user->id]);
    }
}

Feature Tests

Leverage FoundryTestCase for browser tests:

use Zenstruck\Foundry\Test\Factories;
use Zenstruck\Foundry\Test\FoundryTestCase;

class PostTest extends FoundryTestCase
{
    use Factories;

    public function test_post_creation(): void
    {
        $post = PostFactory::new()->create();
        $this->get('/posts/' . $post->slug)
            ->assertOk();
    }
}

5. Fixtures with DoctrineFixturesBundle

Load factories as fixtures:

# config/packages/doctrine.yaml
doctrine:
    fixtures:
        # ...
        zenstruck_foundry:
            factories:
                - App\Database\Factories\UserFactory
                - App\Database\Factories\PostFactory

Run fixtures:

php bin/console doctrine:fixtures:load --group=zenstruck_foundry

6. Customizing Faker

Override Faker providers globally:

// config/foundry.php
'faker' => [
    'providers' => [
        Faker\Provider\Lorem::class,
        App\Faker\CustomProvider::class,
    ],
],

Or per factory:

final class UserFactory extends ModelFactory
{
    protected function configure(): void
    {
        $this->faker->addProvider(new CustomProvider($this->faker));
    }
}

7. Auto-Refresh

Enable auto-refresh for Doctrine entities (no manual refresh() calls):

// config/foundry.php
'orm' => [
    'auto_refresh' => true,
],

8. Hooks and Events

Use #[AsFoundryHook] for custom logic:

#[AsFoundryHook('PostFactory::afterInstantiate')]
public function logPostCreation(Post $post): void
{
    \Log::info('Post created: ' . $post->title);
}

Dispatch events:

$post = PostFactory::new()
    ->afterInstantiate(function (Post $post) {
        event(new PostCreated($post));
    })
    ->create();

Gotchas and Tips

1. Doctrine Events and withoutDoctrineEvents()

  • Gotcha: Doctrine events (e.g., prePersist, preUpdate) may interfere with factory creation.
  • Fix: Use withoutDoctrineEvents() to suppress them:
    $user = UserFactory::new()
        ->withoutDoctrineEvents()
        ->create();
    
  • Warning: Avoid using withoutDoctrineEvents() inside flush_after()—it throws an exception in v2.10+.

2. Auto-Refresh Quirks

  • Gotcha: Auto-refresh may cause issues with derived entities or MongoDB ODM.
  • Fix: Disable auto-refresh for specific factories:
    final class UserFactory extends ModelFactory
    {
        public function __construct()
        {
            $this->autoRefresh = false;
        }
    }
    
  • Tip: Use refresh() manually for complex relationships:
    $post = PostFactory::new()->create();
    $this->entityManager->refresh($post);
    

3. Permutations and Data Providers

  • Gotcha: Permutations (combinations of states) can explode test data.
  • Tip: Limit permutations with permutations():
    #[DataProvider('userProvider')]
    public function test_user_permutations(User $user): void
    {
        // ...
    }
    
    public static function userProvider(): array
    {
        return UserFactory::new()
            ->permutations([
                'role' => ['admin', 'user'],
                'status' => ['active', 'suspended'],
            ])
            ->count(2)
            ->randomize()
            ->provide();
    }
    

4. Faker Seed Management

  • Gotcha: Faker seeds can cause inconsistent data across test runs.
  • Fix: Skip seed management for specific factories:
    $user = UserFactory::new()
        ->skipFakerSeed()
        ->create();
    
  • Tip: Set a global seed in .env:
    FOUNDRY_FAKER_SEED=1234
    

5. Database Reset Strategies

  • Gotcha: Resetting the database between tests can be slow.
  • Tip: Use ResetDatabase trait with migrate mode for faster resets:
    #[ResetDatabase(mode: 'migrate')]
    class UserTest extends TestCase
    {
        // ...
    }
    
  • Warning: Avoid schema mode in CI—it’s slow. Use migrate or in-memory instead.

6. Ghost Objects

  • Gotcha: Unpersisted objects ("ghosts") may linger in memory.
  • Fix: Delete them explicitly:
    $user = UserFactory::new()->create(['email' => '[email protected]']);
    UserFactory::delete($user);
    
  • Tip: Use Foundry::cleanup() to purge all ghosts:
    Foundry::clean
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource