# Database Factory

## Introduction[​](#introduction "Direct link to Introduction")

Models can use factories to automatically generate data for your application. They're incredibly useful to get 'real' data in place without the bloat of a database dump.

Out of the box, a model does not need a factory defined. It will fallback to the core factory for the respective model type. For example, a post model will fallback to the Mantle default post factory. However, if you want to define a factory for a model, you can do so. See [Defining Custom Factory for a Model](#defining-custom-factory-for-a-model) for more information.

## Creating Models Using Factories[​](#creating-models-using-factories "Direct link to Creating Models Using Factories")

You can use the `factory()` method on a model to retrieve the factory for the respective model and start creating models.

```php
use App\Models\Post;

Post::factory()->create(); // int

```

Factories can also be used statically within a [Mantle Teskit test case](/docs/testing/testkit.md):

```php
use Mantle\Testkit\TestCase as Testkit;

class ExampleTest extends Testkit {
  public function test_factory() {
    $post = static::factory()->post->create_and_get(); // WP_Post.

    $term_id = static::factory()->tag->create(); // int

    // ...
  }
}

```

### Create a Single Instance[​](#create-a-single-instance "Direct link to Create a Single Instance")

Create a model from the factory definition and optionally override it with your own arguments with `create()`. Returns the ID of the created model.

* Framework Use
* Testkit Use

```php
use App\Models\Post;

Post::factory()->create(); // int

Post::factory()->create(
  [
    'post_title' => 'My Custom Title',
  ]
);

```

```php
use App\Models\Post;
use Mantle\Testkit\TestCase as Testkit;

class ExampleTest extends Testkit {
  public function test_factory() {
    $post = static::factory()->post->create_and_get(); // WP_Post.

    $term_id = static::factory()->tag->create(); // int

    // ...
  }
}

```

### Create Multiple Instances[​](#create-multiple-instances "Direct link to Create Multiple Instances")

Create multiple models from the factory definition and optionally override it with your own arguments using the `count()`, `create_many()`, or `create_many_and_get()` methods. It will return an array of IDs of the created models.

* Framework Use
* Testkit Use

```php
use App\Models\Post;

Post::factory()->count( 3 )->create(); // int[]

Post::factory()->count( 3 )->create( [
  'post_title' => 'My Custom Title',
] );

$instance = Post::factory()->count( 3 )->create_and_get(); // App\Models\Post[]

```

```php
use App\Models\Post;
use Mantle\Testkit\TestCase as Testkit;

class ExampleTest extends Testkit {
  public function test_factory() {
    $post_ids = static::factory()->post->create_many( 3 ); // int[]
    $term_ids = static::factory()->tag->create_many( 3 ); // int[]

    // create_many_and_get() can also be used to retrieve multiple objects.
    $posts = static::factory()->post->create_many_and_get( 3 ); // WP_Post[]
    $terms = static::factory()->tag->create_many_and_get( 3 ); // WP_Term[]

    // You may use the fluent count() method to create multiple models, too.
    $post_ids = static::factory()->post->count( 3 )->create(); // int[]
    $term_ids = static::factory()->tag->count( 3 )->create(); // int[]


    // ...
  }
}

```

### Create and Get a Model Instance[​](#create-and-get-a-model-instance "Direct link to Create and Get a Model Instance")

Create a model from the factory definition and optionally override it with your own arguments using `create_and_get()`. It will return the created model/core object or an array of created models if chained with `count()`:

* Framework Use
* Testkit Use

```php
use App\Models\Post;

Post::factory()->create_and_get(); // \App\Models\Post

Post::factory()->create_and_get(
  [
    'post_title' => 'My Custom Title',
  ]
);

```

```php
use App\Models\Post;
use Mantle\Testkit\TestCase as Testkit;

class ExampleTest extends Testkit {
  public function test_factory() {
    $post = static::factory()->post->create_and_get(); // WP_Post

    $term = static::factory()->tag->create_and_get(); // WP_Term

    // count() can be used to create multiple models.
    $posts = static::factory()->post->count( 3 )->create_and_get(); // WP_Post[]
    $terms = static::factory()->tag->count( 3 )->create_and_get(); // WP_Term[]

    // ...
  }
}

```

You can also retrieve a model instance instead of WP\_Post/WP\_Term by chaining the `as_models()` method to the `create_and_get()` method:

```php
use App\Models\Post;
use Mantle\Testkit\TestCase as Testkit;

class ExampleTest extends Testkit {
  public function test_factory() {
    $post = static::factory()->post->as_models()->create_and_get(); // Mantle\Database\Model\Post

    $term = static::factory()->tag->as_models()->create_and_get(); // Mantle\Database\Model\Term

    // ...
  }
}

```

The return value of `create_and_get()` depends on how the factory is used

`create_and_get()` will return either the model instance or the underlying "core object" (WP\_Post/WP\_User/WP\_Term/etc.) depending on the configuration of the factory. When a factory is used via the `Model::factory()` method, `create_and_get()` will return the model instance. When a factory is used via the `static::factory()->post` method in unit tests, `create_and_get()` will return the underlying "core object" (WP\_Post/WP\_User/WP\_Term/etc.).

### Retrieving or Creating a Model Instance[​](#retrieving-or-creating-a-model-instance "Direct link to Retrieving or Creating a Model Instance")

You can use the `first_or_create()` method to retrieve an existing model or create a new one if it doesn't exist. This is useful when you want to ensure that a model exists in the database without having to check for its existence first.

* Framework Use
* Testkit Use

```php
use App\Models\Post;

$post = Post::factory()->first_or_create(
  [
    'post_title' => 'My Custom Title',
  ],
  // Additional attributes to set on the post if it is created.
  [
    'post_content' => 'This is the content of the post.',
    'post_status'  => 'publish',
  ]
);

```

```php
use App\Models\Post;
use Mantle\Testkit\TestCase as Testkit;

class ExampleTest extends Testkit {
  public function test_factory() {
    $post = static::factory()->post->first_or_create(
      [
        'post_title' => 'My Custom Title',
      ],
      // Additional attributes to set on the post if it is created.
      [
        'post_content' => 'This is the content of the post.',
        'post_status'  => 'publish',
      ]
    );

    // ...
  }
}

```

## Defining Custom Factory for a Model[​](#defining-custom-factory-for-a-model "Direct link to Defining Custom Factory for a Model")

Out of the box, any model that does not have a factory defined will fallback to the core factory for the respective model type.

| Model Type | Core Factory                                |
| ---------- | ------------------------------------------- |
| Attachment | Mantle\Database\Factory\Attachment\_Factory |
| Site       | Mantle\Database\Factory\Blog\_Factory       |
| Comment    | Mantle\Database\Factory\Comment\_Factory    |
| Network    | Mantle\Database\Factory\Network\_Factory    |
| Post       | Mantle\Database\Factory\Post\_Factory       |
| Term       | Mantle\Database\Factory\Term\_Factory       |
| User       | Mantle\Database\Factory\User\_Factory       |

These factories provide a basic definition for the model. If you want to define a custom factory for a model, you can do so by creating a custom factory for your model.

### Generating a Factory[​](#generating-a-factory "Direct link to Generating a Factory")

You can create a factory by adding a new file in the `database/factory` folder. The expected factory name is:

`App\Database\Factory\{Model_Name}_Factory`

This can be generated using the `make:factory` command:

```bash
bin/mantle make:factory {name} {--model_type=} {--object_name=}

```

### Using Custom Model Factory[​](#using-custom-model-factory "Direct link to Using Custom Model Factory")

Once a factory is generated, you can modify the `definition()` method of the factory to define the data that should be generated. The `definition()` method should return an array of data that will be used to create the model.

```php
namespace App\Database\Factory;

use App\Models\Example_Post_Model;

/**
 * Example Post Model Factory
 *
 * @extends \Mantle\Database\Factory\Post_Factory<\App\Models\Example_Post_Model>
 */
class Example_Post_Model_Factory extends \Mantle\Database\Factory\Post_Factory {
  /**
	 * Model to use when creating objects.
	 *
	 * @var class-string
	 */
	protected string $model = Example_Post_Model::class;

	/**
	 * Define the model's default state.
	 *
	 * @return array
	 */
	public function definition(): array {
		return [
			'post_title'   => $this->faker->sentence,
			'post_content' => $this->faker->paragraph,
			'post_status'  => 'publish',
			'post_type'    => 'post',
		];
	}
}

```

### Generating Blocks in Factories[​](#generating-blocks-in-factories "Direct link to Generating Blocks in Factories")

All factories have an instance of [Faker](https://fakerphp.github.io/) available to them on the `$this->faker` property. This can be used to generate blocks using the `block()` method. You can optionally include attributes and content.

```php
$this->faker->block(
	'namespace/block',
	'The Content',
	[
		'exampleAttr' => true,
		'another' => false,
	]
);

```

Which would produce this:

```html
<!-- wp:namespace/block {"exampleAttr":true,"another":false} -->
The Content
<!-- /wp:namespace/block -->

```

Faker also supports generating paragraph blocks using the `paragraph_block()`/`paragraph_blocks()` methods.

```php
$this->faker->paragraph_block( int $sentences = 3 ): string
$this->faker->paragraph_blocks( int $count = 3, bool $as_text = true ): string|array

```

### Factory States[​](#factory-states "Direct link to Factory States")

You can define states for a factory by defining a method on the factory and use the `state()` method to define the state. The `state()` method accepts an array of data to use when creating the model.

```php
use Mantle\Database\Factory\Post_Factory;

public function my_custom_state(): Post_Factory {
  return $this->state(
    [
      'post_title' => 'My Custom Title',
    ]
  );
}

```

You can then use the state when creating a model:

```php
$example_post = Example_Post_Model::factory()->my_custom_state()->create();

```

## Generating Content[​](#generating-content "Direct link to Generating Content")

See [Creating Models Using Factories](#creating-models-using-factories) for more information about the `create()`/`create_many()`/`create_and_get()` methods.

* Model Factory
* Testkit Factory

```php
use App\Models\Post;
use App\Models\Category;

$post_id = Post::factory()
  ->with_meta( [ 'key' => 'value' ] )
  ->create();

$term_id = Category::factory()->tag
  ->with_meta( [ 'key' => 'value' ] )
  ->create();

// ...

```

```php
use App\Models\Post;
use Mantle\Testkit\TestCase as Testkit;

class ExampleTest extends Testkit {

  public function test_factory() {
    $post_id = static::factory()->post
      ->with_meta( [ 'key' => 'value' ] )
      ->create();

    // ...

    $term_id = static::factory()->tag->with_meta(
      [
        'key' => 'value',
      ]
    )->create();

    // ...
  }
}

```

### Generating Posts/Terms with Meta[​](#generating-poststerms-with-meta "Direct link to Generating Posts/Terms with Meta")

Posts and terms can be generated with meta by calling the `with_meta` method on the factory:

* Model Factory
* Testkit Factory

```php
use App\Models\Post;
use App\Models\Category;

$post_id = Post::factory()
  ->with_meta( [ 'key' => 'value' ] )
  ->create();

$term_id = Category::factory()->tag
  ->with_meta( [ 'key' => 'value' ] )
  ->create();

// ...

```

```php
use App\Models\Post;
use Mantle\Testkit\TestCase as Testkit;

class ExampleTest extends Testkit {

  public function test_factory() {
    $post_id = static::factory()->post
      ->with_meta( [ 'key' => 'value' ] )
      ->create();

    // ...

    $term_id = static::factory()->tag->with_meta(
      [
        'key' => 'value',
      ]
    )->create();

    // ...
  }
}

```

### Generating Posts with Terms[​](#generating-posts-with-terms "Direct link to Generating Posts with Terms")

Posts can be generated with terms by calling the `with_terms()` method on the factory:

* Model Factory
* Testkit Factory

```php
use App\Models\Post;
use App\Models\Tag;

$tag_id = Tag::factory()->create();

$post_id = Post::factory()->with_terms(
  // Pass in taxonomy => slug pairs
  [
    'category' => [
      'category_a',
      'category_b',
    ],
  ],

  // Pass in a single taxonomy => slug pair.
  [
    'post_tag' => 'single-tag',
  ],

  // Pass in term ID(s).
  $tag_id,

  // Or pass in term objects.
  Tag::factory()->create_and_get(),
)->create();

// ...

```

```php
use Mantle\Testkit\TestCase as Testkit;

class ExampleTest extends Testkit {

  public function test_factory() {
    $tag_id = static::factory()->tag->create();

    $post_id = static::factory()->post->with_terms(
      // Pass in taxonomy => slug pairs
      [
        'category' => [
          'category_a',
          'category_b',
        ],
      ],

      // Pass in a single taxonomy => slug pair.
      [
        'post_tag' => 'single-tag',
      ],

      // Pass in term ID(s).
      $tag_id,

      // Or pass in term objects.
      static::factory()->tag->create_and_get(),
    )->create();

    // ...
  }
}

```

### Generating Terms with Posts[​](#generating-terms-with-posts "Direct link to Generating Terms with Posts")

Terms can be generated with posts by calling the `with_posts()` method on the term factory:

* Model Factory
* Testkit Factory

```php
use App\Models\Post;
use App\Models\Tag;

$post_id = Post::factory()->create();

$term_id = Tag::factory()->with_posts(
  // Pass in post ID(s).
  $post_id,

  // Or pass in post objects.
  Post::factory()->create_and_get(),
)->create();

// ...

```

```php
use Mantle\Testkit\TestCase as Testkit;

class ExampleTest extends Testkit {

  public function test_factory() {
    $post_id = static::factory()->post->create();

    $term_id = static::factory()->tag->with_posts(
      // Pass in post ID(s).
      $post_id,

      // Or pass in post objects.
      static::factory()->post->create_and_get(),
    )->create();

    // ...
  }
}

```

### Generating Post with Thumbnail[​](#generating-post-with-thumbnail "Direct link to Generating Post with Thumbnail")

Posts can be generated with a thumbnail by calling the `with_thumbnail` method on the post factory:

* Model Factory
* Testkit Factory

```php
use App\Models\Post;

Post::factory()->with_thumbnail()->create_and_get();

```

```php
use Mantle\Testkit\TestCase as Testkit;

class ExampleTest extends Testkit {

  public function test_factory() {
    $post = static::factory()->post->with_thumbnail()->create_and_get();

    // ...
  }
}

```

The underlying post will have an attachment set as the thumbnail (via the `_thumbnail_id` post meta). The attachment will not have a real image file attached to it for performance. If you'd like a real underlying image file, you can use the `with_real_thumbnail()` method:

* Model Factory
* Testkit Factory

```php
use App\Models\Post;

Post::factory()->with_real_thumbnail()->create_and_get();

```

```php
use Mantle\Testkit\TestCase as Testkit;

class ExampleTest extends Testkit {

  public function test_factory() {
    $post = static::factory()->post->with_real_thumbnail()->create_and_get();

    // ...
  }
}

```

See [Generating an Attachment with a Real Image File](#generating-an-attachment-with-a-real-image-file) for more information.

### Generating Posts with a Custom Post Type[​](#generating-posts-with-a-custom-post-type "Direct link to Generating Posts with a Custom Post Type")

Posts can be generated with a custom post type by calling the `with_post_type()` method on the factory:

```php
use Mantle\Testkit\TestCase as Testkit;

class ExampleTest extends Testkit {

  public function test_factory() {
    $post_id = static::factory()->post->with_post_type( 'custom_post_type' )->create();
  }
}

```

### Generating an Ordered Set of Posts[​](#generating-an-ordered-set-of-posts "Direct link to Generating an Ordered Set of Posts")

Mantle includes a helper to create an ordered set of posts that are evenly spaced out. This can be useful when trying to populate a page with a set of posts and want to verify the order of the posts on the page.

* Model Factory
* Testkit Factory

```php
use App\Models\Post;

$post_ids = Post::factory()->create_ordered_set( 10 );

```

```php
use Mantle\Testkit\TestCase as Testkit;

class ExampleTest extends Testkit {

  public function test_create_ordered_set() {
    $post_ids = static::factory()->post->create_ordered_set( 10 );
  }
}

```

The above post IDs are evenly spaced an hour apart starting from a month ago. The start date and the separation can also be adjusted:

```php
use Carbon\Carbon;
use Mantle\Testkit\TestCase as Testkit;

class ExampleTest extends Testkit {

  public function test_create_ordered_set() {
    $post_ids = static::factory()->post->create_ordered_set(
      10,
      [],
      // Start creating the post a year ago.
      Carbon::now()->subYear(),
      // Spread them out by a day.
      DAY_IN_SECONDS,
    );
  }
}

```

### Generating an Attachment with a Real Image File[​](#generating-an-attachment-with-a-real-image-file "Direct link to Generating an Attachment with a Real Image File")

By default, all attachments generated by the factory won't have a "real" file included with it for performance. You can opt to create an attachment with a real image file by calling the `with_image()` method on the attachment factory:

* Model Factory
* Testkit Factory

```php
use App\Models\Attachment;

Attachment::factory()->with_image()->create_and_get();

```

You can also pass a local file to use as the image:

```php
use App\Models\Attachment;

Attachment::factory()->with_image(
  __DIR__ . '/image.jpg'
)->create_and_get();

```

```php
use Mantle\Testkit\TestCase as Testkit;

class ExampleTest extends Testkit {

  public function test_factory() {
    $attachment_id = static::factory()->attachment->with_image()->create();

    // ...
  }
}

```

You can also pass a local file to use as the image:

```php
use Mantle\Testkit\TestCase as Testkit;

class ExampleTest extends Testkit {

  public function test_factory() {
    $attachment_id = static::factory()->attachment->with_image(
      __DIR__ . '/image.jpg'
    )->create();

    // ...
  }
}

```

### Slashing Content[​](#slashing-content "Direct link to Slashing Content")

When generating content, you may want to ensure that the content is slashed correctly. This can prevent errors saving unicode characters in the content of a post.

You can use the `slash()` method on the factory to ensure that the content is slashed correctly:

```php
use Mantle\Testkit\TestCase as Testkit;

class ExampleTest extends Testkit {

  public function test_factory() {
    $block = get_comment_delimited_block_content( 'namespace/block', [
      'class' => 'story story--lg story--float',
    ], null );

    $post_id = static::factory()->post->slash()->create_and_get(
      [
        'post_content' => $block,
      ]
    );

    // The block will now have the proper class attribute. Without slash(),
    // the class attribute would be broken.
  }
}

```

### Factory Middleware[​](#factory-middleware "Direct link to Factory Middleware")

Mantle includes a middleware system that allows you to hook into the factory process and modify the data before it's saved to the database. This can be useful if you want to modify the data before it's saved or if you want to perform some action after the data is saved. The factory itself uses middleware to assign terms to posts after they're saved, set meta, and more.

Middleware can be added to the factory by calling the `with_middleware()` method:

```php
use Mantle\Testkit\TestCase as Testkit;

class ExampleTest extends Testkit {

  protected $custom_factory;

  protected function setUp(): void {
    parent::setUp();

    $this->custom_factory = static::factory()->post->with_middleware(
      function ( array $args, \Closure $next ) {
        // Modify the arguments (if needed).

        // Call the next middleware
        $result = $next( $args );

        // Modify the result (if needed).

        return $result;
      }
  }
}

```
