Most Eloquent factory states I've created are pretty simple like ->published() or ->cancelled().
They flip a flag or override a field to a fixed value making the call site of the factory easier to understand and requiring less ceremony.
Awesome.
But, a state can also accept arguments, and that opens up some useful patterns like deriving values from another model passed in by the caller.
Imagine we had a scheduled event factory, and we want to use it to build chains of events, one hour after another. We might make a state like this:
public function fromEventOneHourLater(ScheduledEvent $source): self
{
return $this->state([
'start_at' => $source->start_at->addHour(),
'timezone' => $source->timezone,
'organizer_email' => $source->organizer_email,
]);
}
The state name says exactly what it does to the data.
The argument is a model because the state needs to read three fields off it, one that gets transformed (start_at plus an hour) and two that get copied verbatim.
We can now build our chain in the test:
$first = ScheduledEvent::factory()->create();
$second = ScheduledEvent::factory()->fromEventOneHourLater($first)->create();
$third = ScheduledEvent::factory()->fromEventOneHourLater($second)->create();
Be careful not to take this too far, though. You should consider this as the facade PHP pattern for reducing your boilerplate test code, not for duplicating business logic.
Here to help,
Aaron
P.S. Efficient use of factories are covered in more detail in our testing workshop. It's the fastest way we know to level up a team's test suite.