SkillAgentSearch skills...

Poser

Create class based model factories in Laravel applications in seconds.

Install / Use

/learn @lukeraymonddowning/Poser
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<p align="center"> <img src="https://github.com/lukeraymonddowning/poser/raw/master/poser-logo.png" width="150"> </p>

Poser

<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->

All Contributors

<!-- ALL-CONTRIBUTORS-BADGE:END -->

Software License

Laravel Class-based Model Factories in literal seconds! Write tests that look as sexy as this...

/** @test */
public function a_user_can_have_customers()
{
    UserFactory::times(20)
               ->hasAddress()
               ->withCustomers(CustomerFactory::times(20)->withBooks(5))();

    $this->assertCount(20 * 20 * 5, Book::all());
}

...with a Factory that looks like this...

namespace Tests\Factories;

use Lukeraymonddowning\Poser\Factory;

class UserFactory extends Factory {

    // No need to write any code here
    
}

Note for Laravel 8 users

Whilst Poser is tagged for Laravel 8, you should start migrating to use Laravel's new built in class based factories. The syntax is nearly identical and you'll pick it up in no time. I want to thank everybody who used, tested, altered and discussed Poser. Its been so much fun to build something to the Laravel community!

Examples

Want to see what Poser looks like compared with the default Laravel tests? Check out our examples!

Install

First, install into your Laravel project using composer.

composer require lukeraymonddowning/poser

Next, publish the Poser config file by calling

php artisan vendor:publish --tag=poser

Lumen Installation

Create the poser.php in your config directory and copy the lukeraymonddowning/poser/src/config/poser.php into it

Add $app->configure('poser'); to the config section in boostrap/app.php

Add $app->register(\Lukeraymonddowning\Poser\PoserServiceProvider::class); to the providers section in boostrap/app.php

Getting Started

To get started quickly, we provide a php artisan make:poser command. You may pass the desired name of your factory as an argument. So the command to create the UserFactory would be php artisan make:poser UserFactory.

If you want to let Poser do all of the work, simply call php artisan make:poser to turn all the models defined in your poser.models_namespace config entry into Poser Factories.

More of a visual person? Watch this video demonstration of Poser

Usage

Poser takes all of the boilerplate out of writing class-based model factories. To get started, install Poser and go to your test suite. Please note: Poser uses the database (obviously), so make sure your test class extends Laravel's TestCase, not PhpUnit's.

The Basics

Let's imagine you have a user model that has many customers...

<?php

namespace App;

class User extends Authenticatable
{

    // ...a little while later

    public function customers()
    {
        return $this->hasMany(Customer::class);
    }
}

To set up the factory for this, create a class (we suggest a 'Factories' directory in your 'tests' folder) called UserFactory (you can also just call it User, but we think the Factory suffix helps), and a class called CustomerFactory.

Both of these classes should extend the Poser/Factory abstract class. Poser can take care of this for you via the make:poser command, so you can call php artisan make:poser UserFactory and php artisan make:poser CustomerFactory.

You should also have CustomerFactory and UserFactory as entries in your database/factories directory (standard Laravel stuff)

Now, head to the test you want to write, and type the following:

/** @test */
public function user_has_customers()
{
    $user = UserFactory::new()
        ->withCustomers(CustomerFactory::times(30))
        ->create();

    $this->assertCount(30, $user->customers);
}

The test should pass with flying colors. Hurrah! Notice that we didn't have to implement the withCustomers() method: Poser was able to intelligently decide what we were trying to do.

For HasOne or HasMany relationships, you can simply prepend with to the relationship method in the model (eg: the customers() method in the User model becomes withCustomers in the tests), and Poser will do the rest.

Let's add a little more complexity: each customer can own many books...

class Customer extends Model
{

    public function books()
    {
        return $this->hasMany(Book::class);
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }

}

So far, so good. Let's create another factory class, this time called BookFactory, that again extends Poser's abstract Factory class. That's all there is to it! Modify your original test to give our customers 5 books each...

/** @test */
public function user_has_customers()
{
    $user = UserFactory::new()
        ->withCustomers(
            CustomerFactory::times(30)->withBooks(BookFactory::times(5))
        )
        ->create();

    $this->assertCount(30, $user->customers);
    $this->assertCount(150, Book::all());
}

...and watch the tests pass. Pretty nice, huh?

Magic Bindings

If your model relationship method name (ie: the customers() method on our User model) is the same or a plural version of our Factory class (ie: CustomerFactory), then we can take advantage of Magic Bindings in Poser.

Let's take another look at our User/Customer example.

/** @test */
public function user_has_customers()
{
    $user = UserFactory::new()
        ->withCustomers(CustomerFactory::times(30))
        ->create();

    $this->assertCount(30, $user->customers);
}

Poser is smart enough to be able to work out that withCustomers() is a reference to the CustomerFactory, and allows us to rewrite our test like this:

/** @test */
public function user_has_customers()
{
    $user = UserFactory::new()
        ->withCustomers(30)
        ->create();

    $this->assertCount(30, $user->customers);
}

The first argument passed to withCustomers() is the number of customers we want to create, in this case: 30.

Imagine, for a contrived example, that every customer should be called "Joe Bloggs". We can pass a second argument to withCustomers() that defines an associative array of column names and values, just like we do with the create(), make() and withAttributes() methods:

/** @test */
public function user_has_customers()
{
    $user = UserFactory::new()
        ->withCustomers(30, [
            "name" => "Joe Bloggs"
        ])
        ->create();

    $this->assertCount(30, $user->customers);
}

To take this a step further, imagine that we want 10 of our customers to be called "Joe Bloggs", 10 to be called "Jane Bloggs" and 10 to be called "John Doe". Poser allows multiple attribute arrays to be passed to the withAttributes, make and create methods, along with any method using magic bindings, to facilitate this:

/** @test */
public function user_has_customers()
{
    $user = UserFactory::new()
        ->withCustomers(30, ["name" => "Joe Bloggs"], ["name" => "Jane Bloggs"], ["name" => "John Doe"])
        ->create();

    $this->assertCount(10, $user->customers->filter(fn($customer) => $customer->name == "Joe Bloggs"));
    $this->assertCount(10, $user->customers->filter(fn($customer) => $customer->name == "Jane Bloggs"));
    $this->assertCount(10, $user->customers->filter(fn($customer) => $customer->name == "John Doe"));
}

For HasOne relationships, like our User's Address, we can do very much the same:

/** @test */
public function user_has_address()
{
    $user = UserFactory::new()
        ->withAddress()
        ->create();

    $this->assertNotEmpty($user->address);
}

We can also pass an array of attributes, but in this case we pass it as the first argument:

/** @test */
public function user_has_address()
{
    $user = UserFactory::new()
        ->withAddress([
            "line_1" => "1 Test Street" 
        ])
        ->create();

    $this->assertNotEmpty($user->address);
}

Sometimes, with[RelationshipMethodName] might not be the most readable choice. Poser also supports the has[RelationshipMethodName] syntax, like so:

/** @test */
public function user_has_address()
{
    $user = UserFactory::new()
        ->hasAddress([
            "line_1" => "1 Test Street" 
        ])
        ->create();

    $this->assertNotEmpty($user->address);
}

In most cases, we don't even need to call create, Poser will call it for us when we try to access a method or property on the model or collection we have built (in this case, the $user property).

/** @test */
public function user_has_address()
{
    $user = UserFactory::new()
        ->hasAddress([
            "line_1" => "1 Test Street" 
        ]);

    $this->assertNotEmpty($user->address); // When we access the $address property, Poser automatically calls `create` for us.
}

Let's now put this all together, and demonstrate how simple it is to world build in Poser. Imagine we want 10 Users, each with an Address and 20 customers. Each customer should have 5 books. That should be 10 Users, 10 Addresses, 200 Customers and 1000 Books. Check it out:

/** @test */
public function users_with_addresses_can_have_customers_with_books() {
    UserFactory::times(10)
               ->hasAddress()
               ->withCustomers(CustomerFactory::times(20)->withBooks(5))();

    $this->assertCount(1000, Book::all());
    $this->assertCount(200, Customer::all());
    $this->assertCount(10, User::all());
    $this->assertCount(10, Address:

Related Skills

View on GitHub
GitHub Stars275
CategoryDevelopment
Updated1mo ago
Forks10

Languages

PHP

Security Score

95/100

Audited on Feb 10, 2026

No findings