Factory Muffin

repository·master·Indexed 19 days ago

https://github.com/thephpleague/factory-muffin

A PHP package for the rapid creation of objects for testing purposes, inspired by the Ruby 'factory_girl' library. It allows developers to define model factories, generate realistic data using Faker, handle relationships, and seed multiple instances. The library provides methods to create persisted models via create(), in-memory instances via instance(), and bulk generation via seed(), with built-in support for cleaning up test data using deleteSaved().

Tokens
8.5K
Snippets
34
Records
40
Agent score
68%

What's inside factory-muffin

  1. Understand state tracking changes in 2.1.x

    master

    In 2.1.x, Factory Muffin distinguishes between objects that are actually in the database and those that are merely queued for saving.

    FunctionBehavior in 2.1.x
    saved()Returns an array of objects actually saved to the database.
    isSaved()Checks if the object is actually saved to the database.
    pending()(New) Returns an array of objects that will be saved later but haven't been yet.
    isPending()(New) Checks if an object is queued to be saved but not yet persisted.
    isPendingOrSaved()(New) Checks if an object is either saved or pending. This matches the old 2.0 isSaved behavior.
  2. Use Callable and Faker generators

    master

    When defining attributes, you can use several types of generators:

    1. Callables/Closures

    Use a closure to generate values dynamically. The closure receives the model instance and a boolean indicating if it is being saved.

    $fm->define('MyModel')->setDefinitions([
        'slug' => function ($object, $saved) {
            return strtolower(trim(preg_replace("/[^a-zA-Z0-9\/|_|+ -]/", '', $object->title), '-'));
        },
    ]);

    You can also use a string representing a static method: 'MyModel::exampleMethod'.

    2. Faker Facade

    Use League\FactoryMuffin\Faker\Facade to generate realistic data.

    • Standard: Faker::word(), Faker::email(), Faker::firstNameMale(), etc.
    • Unique: Faker::unique()->firstNameFemale() ensures the value is unique across generated models.
    • Optional: Faker::optional()->imageUrl(400, 400) may return null instead of a value.
    • Ranges: Faker::numberBetween(20, 40).

    3. Factory Generator (Relationships)

    To set up relationships, use the factory|ModelName syntax. This returns the ID of the newly generated related model.

    $fm->define('Foo')->addDefinition('bar_id', 'factory|Bar');
    use League\FactoryMuffin\Faker\Facade as Faker;
    
    $fm->define('MyModel')->setDefinitions([
        'title' => Faker::sentence(5),
        'slug'  => function ($object, $saved) {
            return strtolower(trim(preg_replace("/[^a-zA-Z0-9\/|_|+ -]/", '', $object->title), '-'));
        },
        'email' => Faker::email(),
        'age'   => Faker::numberBetween(20, 40),
        'name'  => Faker::unique()->firstNameFemale(),
        'pic'   => Faker::optional()->imageUrl(400, 400),
    ]);
    
    // Relationship example
    $fm->define('Foo')->addDefinition('bar_id', 'factory|Bar');
  3. Replace Facade with FactoryMuffin instances in 3.x

    master

    In version 3.x, the Facade class has been removed. All static methods are now instance methods on the FactoryMuffin class. You must instantiate FactoryMuffin manually and manage its lifecycle (e.g., storing it as a property in your base test case).

    Example Migration:

    2.x (Static):

    static function setupBeforeClass()
    {
        Facade::loadFactories(__DIR__ . '/factories');
    }
    
    Facade::create('ShinyObject');

    3.x (Instance):

    function setUp()
    {
        parent::setUp();
    
        $this->fm = new FactoryMuffin();
        $this->fm->loadFactories(__DIR__ . '/factories');
    }
    
    $this->fm->create('ShinyObject');
    $this->fm = new FactoryMuffin();
    $this->fm->loadFactories(__DIR__ . '/factories');
    $this->fm->create('ShinyObject');
  4. Upgrade from 1.4.x to 1.5.x

    master

    This version introduces specific exceptions and changes how factories are defined.

    Key Changes:

    • New Exceptions:
      • Zizaco\FactoryMuff\SaveException: Thrown if the model's save function returns false during creation.
      • Zizaco\FactoryMuff\NoDefinedFactoryException: Thrown if you attempt to generate attributes for a model that has no defined factory (previously a fatal error).
    • Factory Definitions: The public static $factory property on models is deprecated. You should switch to using the define method: Zizaco\FactoryMuff\Facade\FactoryMuff::define('Fully\Qualified\ModelName', ['foo' => 'bar']).

    Installation (v1.5)

    Update your composer.json:

    {
        "require-dev": {
            "league/factory-muffin": "1.5.*
        }
    }
  5. Upgrade from 1.6.x to 2.0.x

    master

    Version 2.0 is a major milestone involving a rename to 'Factory Muffin' and significant breaking changes.

    Key Changes:

    • Namespace Change: The root namespace moved from Zizaco actorymuff to League actorymuffin. Access the facade via League\FactoryMuffin\Facade::fooBar().
    • Autoloading: Moved from PSR-0 to PSR-4.
    • Facade: Now uses __callStatic for dynamic calls and returns the factory instance from public methods to support method chaining.
    • Factory Definitions: Public static $factory properties on models are no longer supported. You must use the define function.
    • Generators (formerly Kinds): Many specific generator classes (Date, Integer, Name, String, Text) were removed in favor of Faker-based alternatives via the Generic generator.
    • PHP Requirement: Minimum version increased to PHP 5.3.3.

    Migration Steps:

    1. Update Class Names and Namespaces

    • Zizaco\FactoryMuff\FactoryMuff $\rightarrow$ League\FactoryMuffin\Factory
    • Zizaco\FactoryMuff\Facade\FactoryMuff $\rightarrow$ League\FactoryMuffin\Facade
    • Zizaco\FactoryMuff\SaveException $\rightarrow$ League\FactoryMuffin\Exceptions\SaveFailedException
    • Zizaco\FactoryMuff\NoDefinedFactoryException $\rightarrow$ League\FactoryMuffin\Exceptions\NoDefinedFactoryException
    • Zizaco\FactoryMuff\Kind $\rightarrow$ League\FactoryMuffin\Generators\Base
    • Zizaco\FactoryMuff\Kind\Call $\rightarrow$ League\FactoryMuffin\Generators\Call
    • Zizaco\FactoryMuff\Kind\Closure $\rightarrow$ League\FactoryMuffin\Generators\Closure
    • Zizaco\FactoryMuff\Kind\Factory $\rightarrow$ League\FactoryMuffin\Generators\Factory
    • Zizaco\FactoryMuff\Kind\Generic $\rightarrow$ League\FactoryMuffin\Generators\Generic

    2. Update Factory Definitions

    Replace static properties with the define method:

    League\FactoryMuffin\Facade::define('Fully\Qualified\ModelName', ['foo' => 'bar']);

    To load multiple definitions in tests, use loadFactories in your setupBeforeClass method:

    League\FactoryMuffin\Facade::loadFactories(__DIR__ . '/factories');

    Note: loadFactories throws League\FactoryMuffin\Exceptions\DirectoryNotFoundException if the directory is missing.

    3. Update Generators (Kinds)

    Use Faker-compatible names via the generic generator. Examples:

    • integer|8 $\rightarrow$ randomNumber|8
    • string $\rightarrow$ sentence or word
    • name $\rightarrow$ firstNameMale (or other Faker names)
    • date and text remain the same.

    New syntax features:

    • Use ; to send multiple arguments to generators.
    • Prefix definitions with unique: or optional: for unique/optional attributes.

    4. Update Model Creation and Seeding

    • create(): Now saves anything generated with the Factory generator.
    • seed($count, ...): A new function that calls create() repeatedly for the specified $count.
    • saved(): Returns an array of all saved objects.
    • isSaved($model): Checks if a model instance is saved.
    • setSaveMethod($method): Set a custom save function.
    • setDeleteMethod($method): Set a custom delete function.

    5. Update Deletion

    • deleteSaved(): Deletes all saved models. Recommended to call this in PHPUnit's tearDownAfterClass.
    • If deletion fails, DeletingFailedException is raised. Use getExceptions() on the exception to retrieve the array of underlying exceptions.

    6. Fix Breaking API Changes

    • attributesFor() and generateAttr(): No longer accept a class name as the first/second argument. You must pass an actual model instance instead.

    Installation (v2.0)

    Update your composer.json:

    {
        "require-dev": {
            "league/factory-muffin": "2.0.*
        }
    }
  6. Install Factory Muffin via Composer

    master

    To install Factory Muffin, ensure you have PHP 5.4+ and Composer installed. Add league/factory-muffin to your require-dev section in composer.json.

    If you want to enable Faker support for generating random data, you must also install league/factory-muffin-faker.

    {
        "require-dev": {
            "league/factory-muffin": "^3.3",
            "league/factory-muffin-faker": "^2.3"
        }
    }
  7. Upgrade from 2.0.x to 2.1.x

    master

    Version 2.1.x introduced support for multiple factory definitions, new model creation checks, and changes to how saved/pending states are tracked.

    Key Changes:

    • Multiple Factory Definitions: Use a group prefix (e.g., group:ModelName) to define multiple definitions for the same model. The group definition overrides the base definition.
    • Model Validation: If a class does not exist when attempting to create a model, a League\FactoryMuffin\Exceptions\ModelNotFoundException is thrown.
    • State Tracking: The behavior of saved() and isSaved() has changed to be more precise regarding database persistence.
  8. Upgrade from 1.5.x to 1.6.x

    master

    This version introduces the faker package and removes Zizaco\FactoryMuff\Wordlist.

    Key Changes:

    • Faker Integration: Most definitions now fall back to the Faker library. This allows for much more diverse data generation.
    • Closures: You can now use closures to generate completely custom attributes.
    • Namespace: Generators/Kinds are located under Zizaco\FactoryMuff\Kind.

    Installation (v1.6)

    Update your composer.json:

    {
        "require-dev": {
            "league/factory-muffin": "1.6.*
        }
    }
  9. Upgrade from 2.1.x to 3.0.x

    master

    Upgrading to version 3.0.x involves several breaking changes, most notably the removal of the static Facade class and changes to how factories are defined.

    Key Changes:

    • Faker Support: Integration with Faker has moved to a separate repository: thephpleague/factory-muffin-faker.
    • Model Deletion: Saved models are now deleted in reverse order of being saved to ensure relationship integrity.
    • Exceptions: Exception handling has been cleaned up and reviewed.
  10. Initialize Factory Muffin

    master

    To use Factory Muffin, instantiate the League\FactoryMuffin\FactoryMuffin class. You can optionally provide a custom StoreInterface implementation for persistence and a GeneratorFactory for attribute generation. If no arguments are provided, it defaults to using ModelStore and a standard GeneratorFactory.

    use League\FactoryMuffin\FactoryMuffin;
    use League\FactoryMuffin\Stores\ModelStore;
    use League\FactoryMuffin\Generators\GeneratorFactory;
    
    // Default initialization
    $fm = new FactoryMuffin();
    
    // Custom initialization
    $fm = new FactoryMuffin(new ModelStore(), new GeneratorFactory());