All Projects → ergebnis → Factory Bot

ergebnis / Factory Bot

Licence: mit
🤖 Provides a fixture factory for doctrine/orm entities.

Projects that are alternatives of or similar to Factory Bot

Orm Pack
A Symfony Pack for Doctrine ORM
Stars: ✭ 1,850 (+3145.61%)
Mutual labels:  orm, doctrine
Doctrinejsonfunctions
Doctrine DQL functions for SQL JSON data type
Stars: ✭ 325 (+470.18%)
Mutual labels:  orm, doctrine
Mikro Orm
TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, MariaDB, PostgreSQL and SQLite databases.
Stars: ✭ 3,874 (+6696.49%)
Mutual labels:  orm, entity
Hunt Entity
An object-relational mapping (ORM) framework for D language (Similar to JPA / Doctrine), support PostgreSQL and MySQL.
Stars: ✭ 51 (-10.53%)
Mutual labels:  orm, entity
Orm
A drop-in Doctrine ORM 2 implementation for Laravel 5+ and Lumen
Stars: ✭ 712 (+1149.12%)
Mutual labels:  orm, doctrine
Dtcqueuebundle
Symfony2/3/4/5 Queue Bundle (for background jobs) supporting Mongo (Doctrine ODM), Mysql (and any Doctrine ORM), RabbitMQ, Beanstalkd, Redis, and ... {write your own}
Stars: ✭ 115 (+101.75%)
Mutual labels:  orm, doctrine
doctrine-json-odm
JSON Object-Document Mapping bundle for Symfony and Doctrine
Stars: ✭ 15 (-73.68%)
Mutual labels:  orm, doctrine
typeorm-factory
Typeorm factory that makes testing easier
Stars: ✭ 28 (-50.88%)
Mutual labels:  factory, entity
Sonatadoctrineormadminbundle
Integrate Doctrine ORM into the SonataAdminBundle
Stars: ✭ 400 (+601.75%)
Mutual labels:  orm, doctrine
Datamappify
Compose, decouple and manage domain logic and data persistence separately. Works particularly great for composing form objects!
Stars: ✭ 338 (+492.98%)
Mutual labels:  orm, entity
Kripton
A Java/Kotlin library for Android platform, to manage bean's persistence in SQLite, SharedPreferences, JSON, XML, Properties, Yaml, CBOR.
Stars: ✭ 110 (+92.98%)
Mutual labels:  orm, entity
Joomla Entity
Semantical API for Joomla!
Stars: ✭ 25 (-56.14%)
Mutual labels:  orm, entity
Pqt
Postgres schema definition, sql/go, code generation package.
Stars: ✭ 65 (+14.04%)
Mutual labels:  orm, entity
Awesome Doctrine
A collection of useful Doctrine snippets.
Stars: ✭ 147 (+157.89%)
Mutual labels:  orm, doctrine
Foundry
A model factory library for creating expressive, auto-completable, on-demand dev/test fixtures with Symfony and Doctrine.
Stars: ✭ 216 (+278.95%)
Mutual labels:  doctrine, factory
Freezer
A simple & fluent Android ORM, how can it be easier ? RxJava2 compatible
Stars: ✭ 326 (+471.93%)
Mutual labels:  orm, entity
Doctrinebehaviors
Doctrine2 behavior traits
Stars: ✭ 782 (+1271.93%)
Mutual labels:  orm, doctrine
Kotgres
SQL generator and result set mapper for Postgres and Kotlin
Stars: ✭ 21 (-63.16%)
Mutual labels:  orm, entity
Think
ThinkPHP Framework ——十年匠心的高性能PHP框架
Stars: ✭ 7,681 (+13375.44%)
Mutual labels:  orm
Lumen Doctrine
Doctrine module for the Lumen PHP framework.
Stars: ✭ 41 (-28.07%)
Mutual labels:  doctrine

factory-bot

Integrate Prune Release Renew

Code Coverage Type Coverage

Latest Stable Version Total Downloads

Provides a fixture factory for doctrine/orm entities.

Installation

Run

$ composer require --dev ergebnis/factory-bot

Usage

The entry point of ergebnis/factory-bot is the FixtureFactory.

You will use the fixture factory to create entity definitions and to create Doctrine entities populated with fake data.

Examples

You can find examples in

Creating a fixture factory

The fixture factory requires an instance of Doctrine\ORM\EntityManagerInterface (for reading class metadata from Doctrine entities, and for persisting Doctrine entities when necessary) and an instance of Faker\Generator for generating fake data.

<?php

use Doctrine\ORM;
use Ergebnis\FactoryBot;
use Faker\Factory;

$entityManager = ORM\EntityManager::create(...);
$faker = Factory::create(...);

$fixtureFactory = new FactoryBot\FixtureFactory(
    $entityManager,
    $faker
);

To simplify the creation of a fixture factory in tests, you can create an abstract test case with access to an entity manager, a faker, and a fixture factory.

<?php

namespace App\Test\Functional;

use Doctrine\ORM;
use Ergebnis\FactoryBot;
use Faker\Generator;
use PHPUnit\Framework;

abstract class AbstractTestCase extends Framework\TestCase
{
    final protected static function entityManager(): ORM\EntityManagerInterface
    {
          // create entity manager from configuration or fetch it from container

          return $entityManager;
    }

    final protected static function faker(): Generator
    {
        $faker = Factory::create();

        $faker->seed(9001);

        return $faker;
    }

    final protected static function fixtureFactory(): FactoryBot\FixtureFactory
    {
        $fixtureFactory = new FactoryBot\FixtureFactory(
            static::entityManager(),
            static::faker()
        );

        // create or load entity definitions

        return $fixtureFactory;
    }
}

Creating entity definitions

Now that you have access to a fixture factory, you can create definitions for Doctrine entities.

<?php

use Ergebnis\FactoryBot;
use Example\Entity;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$fixtureFactory->define(Entity\User::class);

This simple definition might work when all entity fields have default values, but typically, you will want to provide a map of entity field names to field definitions.

<?php

use Ergebnis\FactoryBot;
use Example\Entity;
use Faker\Generator;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$fixtureFactory->define(Entity\User::class, [
    'avatar' => FactoryBot\FieldDefinition::reference(Entity\Avatar::class),
    'id' => FactoryBot\FieldDefinition::closure(static function (Generator $faker): string {
        return $faker->uuid;
    }),
    'location' => FactoryBot\FieldDefinition::optionalClosure(static function (Generator $faker): string {
        return $faker->city;
    }),
    'login' => FactoryBot\FieldDefinition::closure(static function (Generator $faker): string {
        return $faker->userName;
    }),
]);

In addition to the map of field names to field definitions, you can specify a closure that the fixture factory will invoke after creating the entity. The closure accepts the freshly created entity and the map of field names to field values that the fixture factory used to populate the entity.

<?php

$closure = static function (object $entity, array $fieldValues): void {
    // ...
};

💡 You can use the closure to modify the freshly created entity.

<?php

use Ergebnis\FactoryBot;
use Example\Entity;
use Faker\Generator;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$fixtureFactory->define(
    Entity\User::class,
    [
        'avatar' => FactoryBot\FieldDefinition::reference(Entity\Avatar::class),
        'id' => FactoryBot\FieldDefinition::closure(static function (Generator $faker): string {
            return $faker->uuid;
        }),
        'location' => FactoryBot\FieldDefinition::optionalClosure(static function (Generator $faker): string {
            return $faker->city;
        }),
        'login' => FactoryBot\FieldDefinition::closure(static function (Generator $faker): string {
            return $faker->userName;
        }),
    ],
    static function (Entity\User $user, array $fieldValues): void {
        if (is_string($fieldValues['location')) {
            // ...
        }
    }
);

Field Definitions

A field definition can be

You can use the FieldDefinition factory to create field definitions shipped with this package or implement the FieldDefinition\Resolvable interface yourself.

💡 Custom field definitions can be useful when you are dealing with identical field definitions over and over again.

Non-nullable fields

When you are working with non-nullable fields, you can use the following field definitions, all of which will resolve to concrete references or values:

Nullable fields

When you are working with nullable fields, you can use the following field definitions, all of which will either resolve to null or to a concrete reference or value (depending on the strategy:

FieldDefinition::closure()

FieldDefinition::closure() accepts a closure.

<?php

use Ergebnis\FactoryBot;
use Faker\Generator;

$closure = static function (Generator $faker, FactoryBot\FixtureFactory $fixtureFactory) {
    // return whatever makes sense
};

The fixture factory will resolve the field definition to the return value of invoking the closure with the instance of Faker\Generator composed into the fixture factory, and the fixture factory itself.

<?php

use Ergebnis\FactoryBot;
use Example\Entity;
use Faker\Generator;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$fixtureFactory->define(Entity\User::class, [
    'id' => FactoryBot\FieldDefinition::closure(static function (Generator $faker): string {
        return $faker->uuid;
    }),
    'organizations' => FactoryBot\FieldDefinition::closure(static function (Generator $faker, FactoryBot\FixtureFactory $fixtureFactory): array {
        return $fixtureFactory->createMany(
            Entity\Organization::class,
            FactoryBot\Count::exact($faker->numberBetween(
                1,
                5
            ))
        );
    }),
]);

/** @var Entity\User $user */
$user = $fixtureFactory->createOne(Entity\User::class);

var_dump($user->id());            // string
var_dump($user->organizations()); // array with 1-5 instances of Entity\Organization

💡 It is possible to specify a closure only (will be normalized to FieldDefinition\Closure):

<?php

use Ergebnis\FactoryBot;
use Example\Entity;
use Faker\Generator;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$fixtureFactory->define(Entity\User::class, [
    'id' => static function (Generator $faker): string {
        return $faker->uuid;
    },
    'organizations' => static function (Generator $faker, FactoryBot\FixtureFactory $fixtureFactory): array {
        return $fixtureFactory->createMany(
            Entity\Organization::class,
            FactoryBot\Count::exact($faker->numberBetween(
                1,
                5
            ))
        );
    },
]);

/** @var Entity\User $user */
$user = $fixtureFactory->createOne(Entity\User::class);

var_dump($user->id());            // string
var_dump($user->organizations()); // array with 1-5 instances of Entity\Organization
FieldDefinition::optionalClosure()

FieldDefinition::optionalClosure() accepts a closure.

<?php

use Ergebnis\FactoryBot;
use Faker\Generator;

$closure = static function (Generator $faker, FactoryBot\FixtureFactory $fixtureFactory) {
    // return whatever makes sense
};

A fixture factory using the Strategy\DefaultStrategy will resolve the field definition to null or to the return value of invoking the closure with the instance of Faker\Generator composed into the fixture factory.

<?php

use Ergebnis\FactoryBot;
use Example\Entity;
use Faker\Generator;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$fixtureFactory->define(Entity\User::class, [
    'location' => FactoryBot\FieldDefinition::optionalClosure(static function (Generator $faker): string {
        return $faker->city;
    }),
]);

/** @var Entity\User $user */
$user = $fixtureFactory->createOne(Entity\User::class);

var_dump($user->location()); // null or a random city

A fixture factory using the Strategy\WithOptionalStrategy will resolve the field definition to the return value of invoking the closure with the instance of Faker\Generator composed into the fixture factory.

<?php

use Ergebnis\FactoryBot;
use Example\Entity;
use Faker\Generator;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$fixtureFactory->define(Entity\User::class, [
    'location' => FactoryBot\FieldDefinition::optionalClosure(static function (Generator $faker): string {
        return $faker->city;
    }),
]);

$withOptionalFixtureFactory = $fixtureFactory->withOptional();

/** @var Entity\User $user */
$user = $withOptionalFixtureFactory->createOne(Entity\User::class);

var_dump($user->location()); // a random city

A fixture factory using the Strategy\WithoutOptionalStrategy will resolve the field definition to null.

<?php

use Ergebnis\FactoryBot;
use Example\Entity;
use Faker\Generator;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$fixtureFactory->define(Entity\User::class, [
    'location' => FactoryBot\FieldDefinition::optionalClosure(static function (Generator $faker): string {
        return $faker->city;
    }),
]);

$withoutOptionalFixtureFactory = $fixtureFactory->withoutOptional();

/** @var Entity\User $user */
$user = $withoutOptionalFixtureFactory->createOne(Entity\User::class);

var_dump($user->location()); // null
FieldDefinition::reference()

FieldDefinition::reference() accepts the class name of an entity or embeddable.

Every fixture factory will resolve the field definition to an instance of the entity or embeddable class populated through the fixture factory.

<?php

use Ergebnis\FactoryBot;
use Example\Entity;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$fixtureFactory->define(Entity\User::class, [
    'avatar' => FactoryBot\FieldDefinition::reference(Entity\Avatar::class),
]);

/** @var Entity\User $user */
$user = $fixtureFactory->createOne(Entity\User::class);

var_dump($user->avatar()); // an instance of Entity\Avatar

❗️ When resolving the reference, the fixture factory needs to be aware of the referenced entity or embeddable.

FieldDefinition::optionalReference()

FieldDefinition::optionalReference() accepts the class name of an entity or embeddable.

A fixture factory using the Strategy\DefaultStrategy] will resolve the field definition to null or an instance of the entity or embeddable class populated through the fixture factory.

<?php

use Ergebnis\FactoryBot;
use Example\Entity;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$fixtureFactory->define(Entity\Repository::class, [
    'template' => FactoryBot\FieldDefinition::optionalReference(Entity\Repository::class),
]);

/** @var Entity\Repository $repository */
$repository = $fixtureFactory->createOne(Entity\Repository::class);

var_dump($repository->template()); // null or an instance of Entity\Repository

A fixture factory using the Strategy\WithOptionalStrategy] will resolve the field definition to an instance of the entity or embeddable class populated through the fixture factory.

<?php

use Ergebnis\FactoryBot;
use Example\Entity;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$fixtureFactory->define(Entity\Repository::class, [
    'template' => FactoryBot\FieldDefinition::optionalReference(Entity\Repository::class),
]);

$withOptionalFixtureFactory = $fixtureFactory->withOptional();

/** @var Entity\Repository $repository */
$repository = $withOptionalFixtureFactory->createOne(Entity\Repository::class);

var_dump($repository->template()); // an instance of Entity\Repository

A fixture factory using the Strategy\WithoutOptionalStrategy] will resolve the field definition to null.

<?php

use Ergebnis\FactoryBot;
use Example\Entity;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$fixtureFactory->define(Entity\Repository::class, [
    'template' => FactoryBot\FieldDefinition::optionalReference(Entity\Repository::class),
]);

$withoutOptionalFixtureFactory = $fixtureFactory->withoutOptional();

/** @var Entity\Repository $repository */
$repository = $withoutOptionalFixtureFactory->createOne(Entity\Repository::class);

var_dump($repository->template()); // null

❗️ When resolving the reference, the fixture factory needs to be aware of the referenced entity or embeddable.

FieldDefinition::references()

FieldDefinition::references() accepts the class name of an entity or embeddable and the count of desired references.

You can create the count from an exact number, or minimum and maximum values.

<?php

use Ergebnis\FactoryBot;

$count = FactoryBot\Count::exact(5);

$otherCount = FactoryBot\Count::between(
    0,
    20
);

💡 When you create the count from minimum and maximum values, the fixture factory will resolve its actual value before creating references. This way, you can have variation in the number of references - any number between the minimum and maximum can be assumed.

A fixture factory using the Strategy\DefaultStrategy will resolve the field definition to an array with zero or more instances of the entity or embeddable class populated through the fixture factory. Depending on the value of $count, the array might be empty.

<?php

use Ergebnis\FactoryBot;
use Example\Entity;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$fixtureFactory->define(Entity\Organization::class, [
    'members' => FactoryBot\FieldDefinition::references(
        Entity\User::class,
        FactoryBot\Count::exact(5)
    ),
    'repositories' => FactoryBot\FieldDefinition::references(
        Entity\Repository::class,
        FactoryBot\Count::between(0, 20)
    ),
]);

/** @var Entity\Organization $organization */
$organization = $fixtureFactory->createOne(Entity\Organization::class);

var_dump($organization->members());      // array with 5 instances of Entity\User
var_dump($organization->repositories()); // array with 0-20 instances of Entity\Repository

A fixture factory using the Strategy\WithOptionalStrategy will resolve the field definition to an array containing at least one instance of the entity or embeddable class populated through the fixture factory, unless $count uses an exact value, see FixtureFactory::createMany().

<?php

use Ergebnis\FactoryBot;
use Example\Entity;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$fixtureFactory->define(Entity\Organization::class, [
    'members' => FactoryBot\FieldDefinition::references(
        Entity\User::class,
        FactoryBot\Count::exact(5)
    ),
    'repositories' => FactoryBot\FieldDefinition::references(
        Entity\Repository::class,
        FactoryBot\Count::between(0, 20)
    ),
]);

$withOptionalFixtureFactory = $fixtureFactory->withoutOptional();

/** @var Entity\Organization $organization */
$organization = $withOptionalFixtureFactory->createOne(Entity\Organization::class);

var_dump($organization->members());      // array with 5 instances of Entity\User
var_dump($organization->repositories()); // empty array with 1-20 instances of Entity\Repository

A fixture factory using the Strategy\WithoutOptionalStrategy will resolve the field definition to an empty array, unless $count uses an exact value, see FixtureFactory::createMany().

<?php

use Ergebnis\FactoryBot;
use Example\Entity;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$fixtureFactory->define(Entity\Organization::class, [
    'members' => FactoryBot\FieldDefinition::references(
        Entity\User::class,
        FactoryBot\Count::exact(5)
    ),
    'repositories' => FactoryBot\FieldDefinition::references(
        Entity\Repository::class,
        FactoryBot\Count::between(0, 20)
    ),
]);

$withoutOptionalFixtureFactory = $fixtureFactory->withoutOptional();

/** @var Entity\Organization $organization */
$organization = $withoutOptionalFixtureFactory->createOne(Entity\Organization::class);

var_dump($organization->members());      // array with 5 instances of Entity\User
var_dump($organization->repositories()); // empty array

❗️ When resolving the references, the fixture factory needs to be aware of the referenced entity or embeddable.

FieldDefinition::sequence()

FieldDefinition::sequence() accepts a string containing the %d placeholder at least once and an optional initial number (defaults to 1).

Every fixture factory will resolve the field definition by replacing all occurrences of the placeholder %d in the string with the sequential number's current value. The sequential number will then be incremented by 1 for the next run.

<?php

use Ergebnis\FactoryBot;
use Example\Entity;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$fixtureFactory->define(Entity\User::class, [
    'login' => FactoryBot\FieldDefinition::sequence(
        'user-%d',
        1
    ),
]);

/** @var Entity\User $userOne */
$userOne = $fixtureFactory->createOne(Entity\User::class);

/** @var Entity\User $userTwo */
$userTwo = $fixtureFactory->createOne(Entity\User::class);

var_dump($userOne->login()); // 'user-1'
var_dump($userTwo->login()); // 'user-2'
FieldDefinition::optionalSequence()

FieldDefinition::optionalSequence() accepts a string containing the %d placeholder at least once and an optional initial number (defaults to 1).

A fixture factory using the Strategy\DefaultStrategy will resolve the field definition to null or by replacing all occurrences of the placeholder %d in the string with the sequential number's current value. The sequential number will then be incremented by 1 for the next run.

<?php

use Ergebnis\FactoryBot;
use Example\Entity;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$fixtureFactory->define(Entity\User::class, [
    'location' => FactoryBot\FieldDefinition::optionalSequence(
        'City %d',
        1
    ),
]);

/** @var Entity\User $userOne */
$userOne = $fixtureFactory->createOne(Entity\User::class);

/** @var Entity\User $userTwo */
$userTwo = $fixtureFactory->createOne(Entity\User::class);

var_dump($userOne->location()); // null or 'City 1'
var_dump($userTwo->location()); // null or 'City 1' or 'City 2'

A fixture factory using the Strategy\WithOptionalStrategy will resolve the field definition by replacing all occurrences of the placeholder %d in the string with the sequential number's current value. The sequential number will then be incremented by 1 for the next run.

<?php

use Ergebnis\FactoryBot;
use Example\Entity;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$fixtureFactory->define(Entity\User::class, [
    'location' => FactoryBot\FieldDefinition::optionalSequence(
        'City %d',
        1
    ),
]);

$withOptionalFixtureFactory = $fixtureFactory->withOptional();

/** @var Entity\User $userOne */
$userOne = $withOptionalFixtureFactory->createOne(Entity\User::class);

/** @var Entity\User $userTwo */
$userTwo = $withOptionalFixtureFactory->createOne(Entity\User::class);

var_dump($userOne->location()); // 'City 1'
var_dump($userTwo->location()); // 'City 2'

A fixture factory using the Strategy\WithoutOptionalStrategy will resolve the field definition to null.

<?php

use Ergebnis\FactoryBot;
use Example\Entity;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$fixtureFactory->define(Entity\User::class, [
    'location' => FactoryBot\FieldDefinition::optionalSequence(
        'City %d',
        1
    ),
]);

$withOptionalFixtureFactory = $fixtureFactory->withOptional();

/** @var Entity\User $userOne */
$userOne = $withOptionalFixtureFactory->createOne(Entity\User::class);

/** @var Entity\User $userTwo */
$userTwo = $withOptionalFixtureFactory->createOne(Entity\User::class);

var_dump($userOne->location()); // null
var_dump($userTwo->location()); // null
FieldDefinition::value()

FieldDefinition::value() accepts an arbitrary value.

The fixture factory will resolve the field definition to the value.

<?php

use Ergebnis\FactoryBot;
use Example\Entity;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$fixtureFactory->define(Entity\User::class, [
    'login' => FactoryBot\FieldDefinition::value('localheinz'),
]);

/** @var Entity\User $user */
$user = $fixtureFactory->createOne(Entity\User::class);

var_dump($user->login()); // 'localheinz'

💡 It is also possible to specify a value only:

<?php

use Ergebnis\FactoryBot;
use Example\Entity;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$fixtureFactory->define(Entity\User::class, [
    'login' => 'localheinz',
]);

/** @var Entity\User $user */
$user = $fixtureFactory->createOne(Entity\User::class);

var_dump($user->login()); // 'localheinz'
FieldDefinition::optionalValue()

FieldDefinition::optionalValue() accepts an arbitrary value.

A fixture factory using the Strategy\DefaultStrategy will resolve the field definition to null or the value.

<?php

use Ergebnis\FactoryBot;
use Example\Entity;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$fixtureFactory->define(Entity\User::class, [
    'location' => FactoryBot\FieldDefinition::optionalValue('Berlin'),
]);

/** @var Entity\User $user */
$user = $fixtureFactory->create(Entity\User::class);

var_dump($user->location()); // null or 'Berlin'

A fixture factory using the Strategy\WithOptionalStrategy will resolve the field definition to the value.

<?php

use Ergebnis\FactoryBot;
use Example\Entity;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$fixtureFactory->define(Entity\User::class, [
    'location' => FactoryBot\FieldDefinition::optionalValue('Berlin'),
]);

$withOptionalFixtureFactory = $fixtureFactory->withOptional();

/** @var Entity\User $user */
$user = $withOptionalFixtureFactory->create(Entity\User::class);

var_dump($user->location()); // 'Berlin'

A fixture factory using the Strategy\WithoutOptionalStrategy will resolve the field definition to null.

<?php

use Ergebnis\FactoryBot;
use Example\Entity;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$fixtureFactory->define(Entity\User::class, [
    'location' => FactoryBot\FieldDefinition::optionalValue('Berlin'),
]);

$withoutOptionalFixtureFactory = $fixtureFactory->withoutOptional();

/** @var Entity\User $user */
$user = $withoutOptionalFixtureFactory->create(Entity\User::class);

var_dump($user->location()); // null

Loading entity definitions

Instead of creating entity definitions inline, you can implement the EntityDefinitionProvider interface and load entity definitions contained within a directory with the fixture factory.

First, create concrete definition providers.

<?php

namespace Example\Test\Fixture\Entity;

use Ergebnis\FactoryBot;
use Example\Entity;

final class UserDefinitionProvider implements FactoryBot\EntityDefinitionProvider
{
    public function accept(FactoryBot\FixtureFactory $fixtureFactory): void
    {
        $fixtureFactory->define(Entity\User::class, [
            // ...
        ]);
    }
}

💡 While you can use a single entity definition provider to provide definitions for all entities, I recommend using one definition provider per entity. Then you can quickly implement an auto-review test to enforce that an entity definition provider exists for each entity.

Second, adjust your abstract test case to load definitions from entity definition providers contained in a directory.

<?php

namespace App\Test\Functional;

use Ergebnis\FactoryBot;
use PHPUnit\Framework;

abstract class AbstractTestCase extends Framework\TestCase
{
    // ...

    final protected static function fixtureFactory(): FactoryBot\FixtureFactory
    {
        $fixtureFactory = new FactoryBot\FixtureFactory(
            static::entityManager(),
            static::faker()
        );

        $fixtureFactory->load(__DIR__ . '/../Fixture');

        return $fixtureFactory;
    }

    // ...
}

Creating entities

Now that you have created (or loaded) entity definitions, you can create Doctrine entities populated with fake data.

The fixture factory allows to create entities using the following strategies:

Strategy\DefaultStrategy Strategy\WithOptionalStrategy Strategy\WithoutOptionalStrategy

Strategy\DefaultStrategy

The Strategy\DefaultStrategy involves random behavior, and based on randomness, the fixture factory might or might not resolve optional field references:

The fixture factory uses the Strategy\DefaultStrategy by default.

Strategy\WithOptionalStrategy

The Strategy\WithOptionalStrategy involves random behavior, but the fixture factory will resolve optional field references:

To create a fixture factory using the Strategy\WithOptionalStrategy out of an available fixture factory, invoke withOptional():

<?php

use Ergebnis\FactoryBot;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$withOptionalFixtureFactory = $fixtureFactory->withOptional();

Strategy\WithoutOptionalStrategy

The Strategy\WithoutOptionalStrategy involves random behavior, but the fixture factory will not resolve optional field references:

To create a fixture factory using the Strategy\WithoutOptionalStrategy out of an available fixture factory, invoke withoutOptional():

<?php

use Ergebnis\FactoryBot;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$withoutOptionalFixtureFactory = $fixtureFactory->withoutOptional();

FixtureFactory::createOne()

FixtureFactory::createOne() accepts the class name of an entity and optionally, a map of entity field names to field definitions that should override the field definitions for that specific entity.

The fixture factory will return a single entity.

<?php

use Ergebnis\FactoryBot;
use Example\Entity;
use Faker\Generator;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$fixtureFactory->define(Entity\User::class, [
    'login' => FactoryBot\FieldDefinition::closure(static function (Generator $faker): string {
        return $faker->userName;
    }),
]);

/** @var Entity\User $userOne */
$userOne = $fixtureFactory->createOne(Entity\User::class);

/** @var Entity\User $userTwo */
$userTwo = $fixtureFactory->createOne(Entity\User::class, [
    'login' => FactoryBot\FieldDefinition::value('localheinz'),
]);

/** @var Entity\User $userThree */
$userThree = $fixtureFactory->createOne(Entity\User::class, [
    'login' => 'ergebnis-bot',
]);

var_dump($userOne->login());   // random user name
var_dump($userTwo->login());   // 'localheinz'
var_dump($userThree->login()); // 'ergebnis-bot'

A field definition override can be

  • an implementation of FieldDefinition\Resolvable
  • a closure (will be normalized to FieldDefinition\Closure)
  • an arbitrary value (will be normalized to FieldDefinition\Value)

Also see Creating entity definitions.

FixtureFactory::createMany()

FixtureFactory::createMany() accepts the class name of an entity, the count of desired entities, and an optional map of entity field names to field definitions that should override the field definitions for that specific entity.

You can create the count from an exact number:

<?php

use Ergebnis\FactoryBot;

$count = FactoryBot\Count::exact(5);

The fixture factory will resolve $count to 5.

You can also create the count from minimum and maximum values.

<?php

use Ergebnis\FactoryBot;

$count = FactoryBot\Count::between(
    0,
    20
);

The fixture factory will resolve $count to any number between 0 and 20.

The fixture factory will return an array of entities.

<?php

use Ergebnis\FactoryBot;
use Example\Entity;
use Faker\Generator;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$fixtureFactory->define(Entity\User::class, [
    'login' => FieldDefinition\Closure(static function (Generator $faker): string {
        return $faker->username;
    }),
]);

/** @var array<Entity\User> $users */
$users = $fixtureFactory->createMany(
    Entity\User::class,
    FactoryBot\Count::exact(5)
);

/** @var array<Entity\User> $otherUsers */
$otherUsers = $fixtureFactory->createMany(
    Entity\User::class,
    FactoryBot\Count::exact(5),
    [
        'login' => FactoryBot\FieldDefinition::sequence('user-%d'),
    ]
);

$normalize = static function (array $users): array {
    return array_map(static function (Entity\User $user): string {
        return $user->login();
    }, $users);
};

var_dump($normalize($users));        // random user names
var_dump($normalize($otherUsers));   // 'user-1', 'user-2', ...

A field definition override can be

  • an implementation of FieldDefinition\Resolvable
  • a closure (will be normalized to FieldDefinition\Closure)
  • an arbitrary value (will be normalized to FieldDefinition\Value)

Also see Creating entity definitions.

Persisting entities

When the fixture factory creates entities, the fixture factory does not persist them by default.

To create a fixture factory that persists entities out of an available fixture factory, invoke persisting():

<?php

use Ergebnis\FactoryBot;

/** @var FactoryBot\FixtureFactory $fixtureFactory */
$persistingFixtureFactory = $fixtureFactory->persisting();

After this point, the fixture factory will automatically persist every entity it creates.

❗️ You need to flush the entity manager yourself.

Flushing entities

The fixture factory will not flush the entity manager - you need to flush it yourself.

Changelog

Please have a look at CHANGELOG.md.

Contributing

Please have a look at CONTRIBUTING.md.

Code of Conduct

Please have a look at CODE_OF_CONDUCT.md.

License

This package is licensed using the MIT License.

Please have a look at LICENSE.md.

Credits

This project is based on breerly/[email protected] (originally licensed under MIT by Grayson Koonce), which is based on xi/doctrine (originally licensed under MIT by Xi), which in turn provided a port of factory_bot (originally licensed under MIT by Joe Ferris and thoughtbot, Inc.).

Curious what I am building?

📬 Subscribe to my list, and I will occasionally send you an email to let you know what I am working on.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].