Weave Code
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

Expressive, autocompletable fixture factories for Symfony + Doctrine (ORM and/or MongoDB). Create random-but-valid entities on demand for fixtures and tests, with states, persistence helpers, and rich testing features via ZenstruckFoundryBundle.

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' => 'john@example.com']);
    

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' => 'temp@example.com']);
    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.
symfony/ai-symfony-mate-extension
aashan/pimcore-mcp-bundle
solution-forest/ai-kit-core
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php