Laravel 8 User factory with role states

State factory methods with user model hooks

Arne
2 min readJan 19, 2021

Crazy! This laravel version 8 with its factories is what i personally waited for a long time. Before we got in touch with this amazing feature we dealt with “testing traits” which did literally the same but with a much bigger overhead.

While migrating our thousandth tests i though it would be great to have a state for each role in our UserFactory. By the way we are also using the amazing package of Spatie Permissions.

Laravel Logo with text: UserFactory with role states
Laravel Logo and article title

The Problem

This is not working:

User::factory()->admin()->create();

with this state method:

/**
* Indicate that the user is an admin.
*
*
@return Factory
*/
public function admin(): UserFactory
{
return $this->assignRole('admin');
}

Its because state methods should return a callback function that is manipulating the $attributes array. Those callbacks are concatenated later in the factory. In this state methods you do not have access to the underlaying model.

Solution

Use the snippet above but now adding a private method to tweak it:

/**
*
@param array|\Spatie\Permission\Contracts\Role|string ...$roles
*
@return UserFactory
*/
private function assignRole(...$roles): UserFactory
{
return $this->afterCreating(fn(User $user) => $user->syncRoles($roles));
}

Now you are using the afterCreating hooks in the states. In the default case you may want to add the callback of your default role to the configure() method. This could may look like this:

/**
* Configure the model factory.
*
*
@return $this
*/
public function configure()
{
return $this->afterMaking(function (User $user) {
return $user->assignRole('user');
});
}

Be patient this assignRole call uses the method within the user model not the factory method. If you have an permission structure where your model could have multiple roles you could change $user->syncRoles($roles) to $user->assignRole($roles).

Now you are ready to use you UserFactoryclass like this:

User::factory()->admin()->create();

--

--

Responses (1)