PHPackages                             zero-to-prod/factory - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. zero-to-prod/factory

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

zero-to-prod/factory
====================

A Generic Factory Pattern for Creating Arrays.

v0.4.0(1y ago)19.0k↓16.7%5MITPHPPHP &gt;=7.1CI passing

Since Feb 15Pushed 8mo ago1 watchersCompare

[ Source](https://github.com/zero-to-prod/factory)[ Packagist](https://packagist.org/packages/zero-to-prod/factory)[ Docs](https://github.com/zero-to-prod/factory)[ Fund](https://github.com/sponsors/zero-to-prod)[ RSS](/packages/zero-to-prod-factory/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (3)Versions (11)Used By (5)

Zerotoprod\\Factory
===================

[](#zerotoprodfactory)

[![](art/logo.png)](art/logo.png)

[![Repo](https://camo.githubusercontent.com/9a90a3efeee26aed7d7f2feee9cd84566a26f9c362cc773b184d076210906e1c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6769746875622d677261793f6c6f676f3d676974687562)](https://github.com/zero-to-prod/factory)[![GitHub Actions Workflow Status](https://camo.githubusercontent.com/6aeeff3ca539599478297d8ab050fbd7dd0effb843c55479c20f399fe985ae55/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f7a65726f2d746f2d70726f642f666163746f72792f746573742e796d6c3f6c6162656c3d74657374)](https://github.com/zero-to-prod/factory/actions)[![GitHub Actions Workflow Status](https://camo.githubusercontent.com/68e0a25b1ffd5ac4b3bbe6dea9c2f68ebe0f6e691e49f1bc42e197a2459f44e2/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f7a65726f2d746f2d70726f642f666163746f72792f6261636b77617264735f636f6d7061746962696c6974792e796d6c3f6c6162656c3d6261636b77617264735f636f6d7061746962696c697479)](https://github.com/zero-to-prod/factory/actions)[![Packagist Downloads](https://camo.githubusercontent.com/377017a55917a6a9e684d425d96a0073be06690ff171d7b5b3b8829551d35388/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f7a65726f2d746f2d70726f642f666163746f72793f636f6c6f723d626c7565)](https://packagist.org/packages/zero-to-prod/factory/stats)[![php](https://camo.githubusercontent.com/98f011e75a53e64c7b29820324088d37710b8b91ffd9c4c6c3fa7bd6a917abca/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f7a65726f2d746f2d70726f642f666163746f72792e7376673f636f6c6f723d707572706c65)](https://packagist.org/packages/zero-to-prod/factory/stats)[![Packagist Version](https://camo.githubusercontent.com/22882ca0c1aae1b766279864ac3293fe16d594154e32d31b068d932b7b0a7154/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f7a65726f2d746f2d70726f642f666163746f72793f636f6c6f723d663238643161)](https://packagist.org/packages/zero-to-prod/factory)[![License](https://camo.githubusercontent.com/e433af1a4e06afa537251cd07c5a9479a5074d3de4b1d19c139cb79c6970e5d6/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f7a65726f2d746f2d70726f642f666163746f72793f636f6c6f723d70696e6b)](https://github.com/zero-to-prod/factory/blob/main/LICENSE.md)[![wakatime](https://camo.githubusercontent.com/5164ea6c8ef1decaa633ac9ac7b3dee69b2457cf4603655a516174549cabacad/68747470733a2f2f77616b6174696d652e636f6d2f62616467652f6769746875622f7a65726f2d746f2d70726f642f666163746f72792e737667)](https://wakatime.com/badge/github/zero-to-prod/factory)[![Hits-of-Code](https://camo.githubusercontent.com/371428ed0f1882ca163cd8eeac6801867962850fcf39d942e359d29d58684d05/68747470733a2f2f686974736f66636f64652e636f6d2f6769746875622f7a65726f2d746f2d70726f642f666163746f72793f6272616e63683d6d61696e)](https://hitsofcode.com/github/zero-to-prod/factory/view?branch=main)

Contents
--------

[](#contents)

- [Introduction](#introduction)
- [Requirements](#requirements)
- [Installation](#installation)
- [Documentation Publishing](#documentation-publishing)
    - [Automatic Documentation Publishing](#automatic-documentation-publishing)
- [Usage](#usage)
    - [Basic Factory Pattern](#basic-factory-pattern)
    - [Model-Based Factory Pattern](#model-based-factory-pattern)
    - [Custom Factory Methods](#custom-factory-methods)
    - [Advanced Factory Features](#advanced-factory-features)
    - [Callable State (Closures)](#callable-state-closures)
    - [The `set()` Method](#the-set-method)
    - [The `merge()` Method](#the-merge-method)
    - [The `context()` Method](#the-context-method)
    - [Context Priority](#context-priority)
    - [The `make()` Method](#the-make-method)
- [Local Development](./LOCAL_DEVELOPMENT.md)
- [Contributing](#contributing)

Introduction
------------

[](#introduction)

A Generic Factory Pattern for Creating Arrays.

Requirements
------------

[](#requirements)

- PHP 7.1 or higher.

Installation
------------

[](#installation)

Install `Zerotoprod\Factory` via [Composer](https://getcomposer.org/):

```
composer require zero-to-prod/factory
```

This will add the package to your project's dependencies and create an autoloader entry for it.

Documentation Publishing
------------------------

[](#documentation-publishing)

You can publish this README to your local documentation directory.

This can be useful for providing documentation for AI agents.

This can be done using the included script:

```
# Publish to default location (./docs/zero-to-prod/factory)
vendor/bin/zero-to-prod-factory

# Publish to custom directory
vendor/bin/zero-to-prod-factory /path/to/your/docs
```

#### Automatic Documentation Publishing

[](#automatic-documentation-publishing)

You can automatically publish documentation by adding the following to your `composer.json`:

```
{
  "scripts": {
    "post-install-cmd": [
      "zero-to-prod-factory"
    ],
    "post-update-cmd": [
      "zero-to-prod-factory"
    ]
  }
}
```

Usage
-----

[](#usage)

### Basic Factory Pattern

[](#basic-factory-pattern)

The simplest factory implementation requires only the `Factory` trait and a `definition()` method:

```
class UserFactory
{
    use \Zerotoprod\Factory\Factory;

    protected function definition(): array
    {
        return [
            'first_name' => 'John',
            'last_name' => 'N/A'
        ];
    }

    public static function factory(array $context = []): self
    {
        return new self($context);
    }
}

// Create an array with default values
$user = UserFactory::factory()->make();
echo $user['first_name']; // 'John'
echo $user['last_name'];  // 'N/A'

// Override specific values
$user = UserFactory::factory(['last_name' => 'Doe'])->make();
echo $user['first_name']; // 'John'
echo $user['last_name'];  // 'Doe'
```

### Model-Based Factory Pattern

[](#model-based-factory-pattern)

You can add a static `factory()` method to your model classes for more convenient usage:

```
class User
{
    public const first_name = 'first_name';
    public $first_name;

    public static function factory(array $context = []): UserFactory
    {
        return new UserFactory($context);
    }
}

class UserFactory
{
    use \Zerotoprod\Factory\Factory;

    protected function definition(): array
    {
        return [
            User::first_name => 'John'
        ];
    }
}

// Call factory directly from the model
$user = User::factory()->make();
echo $user['first_name']; // 'John'

// Pass context to make() method
$user = User::factory()->make([User::first_name => 'Jane']);
echo $user['first_name']; // 'Jane'
```

### Custom Factory Methods

[](#custom-factory-methods)

Create custom methods in your factory to provide a fluent, expressive interface for setting specific values. These methods use the `state()` method internally to modify the factory context.

```
class UserFactory
{
    use \Zerotoprod\Factory\Factory;

    protected function definition(): array
    {
        return [
            'first_name' => 'John',
            'last_name' => 'N/A',
            'address' => [
                'street' => '123 Main St',
            ],
            'shipping_address' => [
                'street' => '123 Main St',
            ]
        ];
    }

    // Simple field setting with array syntax
    public function setFirstName(string $value): self
    {
        return $this->state(['first_name' => $value]);
    }

    // Nested field setting with dot notation
    public function setAddress(string $street): self
    {
        return $this->state('address.street', $street);
    }

    // Complete array replacement
    public function setShippingAddress(array $address): self
    {
        return $this->state('shipping_address', $address);
    }

    public static function factory(array $context = []): self
    {
        return new self($context);
    }
}

// Usage examples
$user = User::factory()
    ->setFirstName('Jim')          // Sets first_name to 'Jim'
    ->setAddress('bogus')          // Sets address.street to 'bogus'
    ->setShippingAddress(['city' => 'NYC']) // Replaces entire shipping_address
    ->make();

echo $user['first_name']; // 'Jim'
echo $user['address']['street']; // 'bogus'
echo $user['shipping_address']['city']; // 'NYC'
```

### Advanced Factory Features

[](#advanced-factory-features)

For more complex scenarios, you can add state manipulation with closures and other advanced patterns:

Define a factory in this way:

```
class UserFactory
{
    use \Zerotoprod\Factory\Factory;

    protected function definition(): array
    {
        return [
            'first_name' => 'John',
            'last_name' => 'N/A',
            'address' => [
                'street' => 'Memory Lane'
            ]
        ];
    }

    public function setStreet(string $value): self
    {
        /** Dot Syntax */
        return $this->state('address.street', $value);
    }

    public function setFirstName(string $value): self
    {
        /** Array Syntax */
        return $this->state(['first_name' => $value]);
    }

    public function setLastName(): self
    {
        /** Closure Syntax - access context values to create dynamic state */
        return $this->state(function ($context) {
            return ['last_name' => $context['first_name']];
        });
    }

    /** Static factory method for fluent instantiation */
    public static function factory(array $context = []): self
    {
        return new self($context);
    }

    /* Optionally implement for better static analysis */
    public function make(array $context = []): array
    {
        return $this->resolve($context);
    }
}

$User = UserFactory::factory([User::last_name => 'Doe'])
            ->setFirstName('Jane')
            ->make();

User::factory([User::last_name => 'Doe'])->make(); // Also works for this example

echo $User['first_name']; // 'Jane'
echo $User['last_name'];  // 'Doe'
```

### Callable State (Closures)

[](#callable-state-closures)

Use closures to create dynamic state based on the current context. This is powerful for creating values that depend on other values in the factory:

```
class UserFactory
{
    use \Zerotoprod\Factory\Factory;

    protected function definition(): array
    {
        return [
            'first_name' => 'John',
            'last_name' => 'N/A'
        ];
    }

    public function setLastName(): self
    {
        return $this->state(function ($context) {
            // Set last_name to the same value as first_name
            return ['last_name' => $context['first_name']];
        });
    }
}

$User = UserFactory::factory()
    ->setLastName()
    ->make();

echo $User['first_name']; // 'John'
echo $User['last_name'];  // 'John' (copied from first_name)
```

The closure receives the current context as its parameter, allowing you to create dynamic relationships between fields.

### The `set()` Method

[](#the-set-method)

The `set()` method provides a flexible way to modify factory state without creating custom methods. It supports four different syntaxes to accommodate various use cases:

#### 1. Key-Value Syntax

[](#1-key-value-syntax)

Set a single field by passing the key and value as separate parameters:

```
->set('first_name', 'John')
->set(User::first_name, 'John')  // Using constants
```

#### 2. Array Syntax

[](#2-array-syntax)

Set multiple fields or use associative arrays:

```
->set(['last_name' => 'Doe'])
->set(['first_name' => 'Jane', 'last_name' => 'Smith'])
```

#### 3. Closure Syntax

[](#3-closure-syntax)

Use closures for dynamic values based on current context:

```
->set(function ($context) {
    return ['surname' => $context['last_name']];
})
```

#### 4. Dot Notation Syntax

[](#4-dot-notation-syntax)

Access and modify nested array values:

```
->set('address.postal_code', '46789')
->set('user.profile.settings.theme', 'dark')
```

#### Complete Example

[](#complete-example)

Here's how all four syntaxes work together:

```
$User = UserFactory::factory()
    ->set('first_name', 'John')                    // Key-value syntax
    ->set(['last_name' => 'Doe'])                  // Array syntax
    ->set(function ($context) {                    // Closure syntax
        return ['surname' => $context['last_name']];
    })
    ->set('address.postal_code', '46789')          // Dot syntax for nested values
    ->make();

echo $User['first_name'];           // John
echo $User['last_name'];            // Doe
echo $User['surname'];              // Doe
echo $User['address']['postal_code']; // 46789
```

### The `merge()` Method

[](#the-merge-method)

The `merge()` method allows you to merge new values directly into the factory's current context. This is useful for bulk updates or overriding multiple values at once.

**Important:** `merge()` updates the context after `set()` methods have been evaluated, so closures will use the original values.

```
class UserFactory
{
    use \Zerotoprod\Factory\Factory;

    protected function definition(): array
    {
        return [
            'first_name' => 'John',
            'last_name' => 'N/A',
            'address' => [
                'postal_code' => 'default'
            ]
        ];
    }
}

$User = UserFactory::factory()
    ->set('first_name', 'John')
    ->set(['last_name' => 'Doe'])
    ->set(function (array $context) {
        // This closure captures 'Doe' when set() is called
        return ['surname' => $context['last_name']];
    })
    ->set('address.postal_code', '46789')
    ->merge(['last_name' => 'merged'])  // Override after set() calls
    ->make();

echo $User['first_name']; // 'John'
echo $User['last_name'];  // 'merged' (from merge())
echo $User['surname'];    // 'Doe' (from closure, before merge())
echo $User['address']['postal_code']; // '46789'
```

Use `merge()` when you want to:

- Apply bulk changes to multiple fields
- Override previously set values
- Update context without creating new state methods

### The `context()` Method

[](#the-context-method)

Use the `context()` method to get the current context of the factory without creating the final result. This is useful for inspecting or debugging the factory state.

```
class UserFactory
{
    use \Zerotoprod\Factory\Factory;

    protected function definition(): array
    {
        return [
            'first_name' => 'John',
            'last_name' => 'N/A',
        ];
    }
}

// Get context with initial values
$context = UserFactory::factory()->context();
echo $context['first_name']; // 'John'
echo $context['last_name'];  // 'N/A'

// Get context with overridden values
$context = UserFactory::factory(['last_name' => 'Doe'])->context();
echo $context['first_name']; // 'John'
echo $context['last_name'];  // 'Doe'
```

### Context Priority

[](#context-priority)

Context can be provided at different stages, with later contexts taking priority:

```
class UserFactory
{
    use \Zerotoprod\Factory\Factory;

    protected function definition(): array
    {
        return [
            'first_name' => 'John',
            'last_name' => 'N/A',
        ];
    }
}

// Context in make() overrides context in factory()
$User = UserFactory::factory(['last_name' => 'Bogus'])
    ->make(['last_name' => 'Doe']);

echo $User['first_name']; // 'John'
echo $User['last_name'];  // 'Doe' (from make(), not 'Bogus')
```

Priority order (highest to lowest):

1. Context passed to `make()` - **Final override at creation time**
2. Values from `merge()` method - **Direct context updates**
3. Context from `state()` methods - **Fluent method calls**
4. Context passed to `factory()` - **Initial context setup**
5. Default values from `definition()` - **Base factory defaults**

**Note:** Closures in `state()` methods are evaluated when the method is called, so they use the context available at that time, before any subsequent `merge()` operations.

### The `make()` Method

[](#the-make-method)

The `make()` method is the final step that creates your data structure. It can accept optional context that will override any previously set values:

```
$factory = UserFactory::factory(['first_name' => 'John'])
    ->set('last_name', 'Smith');

// Create with factory/state values
$user1 = $factory->make();
echo $user1['first_name']; // 'John'
echo $user1['last_name'];  // 'Smith'

// Override at make time
$user2 = $factory->make(['first_name' => 'Jane']);
echo $user2['first_name']; // 'Jane' (overridden)
echo $user2['last_name'];  // 'Smith' (preserved)
```

Contributing
------------

[](#contributing)

Contributions, issues, and feature requests are welcome! Feel free to check the [issues](https://github.com/zero-to-prod/factory/issues) page if you want to contribute.

1. Fork the repository.
2. Create a new branch (`git checkout -b feature-branch`).
3. Commit changes (`git commit -m 'Add some feature'`).
4. Push to the branch (`git push origin feature-branch`).
5. Create a new Pull Request.

###  Health Score

32

—

LowBetter than 71% of packages

Maintenance56

Moderate activity, may be stable

Popularity25

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity28

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

10

Last Release

448d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/502649f05d36c87d494988bd99193a4d908d345335d99c080928a726277371f5?d=identicon)[zero-to-prod](/maintainers/zero-to-prod)

---

Top Contributors

[![zero-to-prod](https://avatars.githubusercontent.com/u/61474950?v=4)](https://github.com/zero-to-prod "zero-to-prod (19 commits)")

---

Tags

factoryphpfactoryzero-to-prod

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/zero-to-prod-factory/health.svg)

```
[![Health](https://phpackages.com/badges/zero-to-prod-factory/health.svg)](https://phpackages.com/packages/zero-to-prod-factory)
```

###  Alternatives

[zero-to-prod/data-model

Transforms Data into Type-Safe DTOs.

14226.2k32](/packages/zero-to-prod-data-model)[aura/dispatcher

Creates objects from a factory and invokes methods using named parameters; also provides a trait for invoking closures and object methods with named parameters.

3695.0k3](/packages/aura-dispatcher)[nikolaposa/cascader

Utility for creating objects in PHP from constructor parameters definitions.

12237.5k3](/packages/nikolaposa-cascader)[illuminatech/array-factory

Allows DI aware object creation from array definition

2159.6k5](/packages/illuminatech-array-factory)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
