In a Laravel model factory, how do states and sequence() vary generated records, and how do you define a reusable named state?
answer
- layers merged over definition()
- method returning $this->state(...)
- closure receives attributes so far
- round-robin: index modulo count
- create() arguments applied last
basics
~20 sA state is an attribute override layered on top of definition(); a named state is a factory method that returns $this->state(...). sequence() adds a state that cycles through its values one record at a time.
solid answer
~40 s`definition()` gives the defaults; `state([...])` or `state(fn (array $attributes) => [...])` merges overrides on top, and later states win. A **named state** is a method on the factory, such as `cancelled()`, that returns `$this->state(...)` so it chains: `Appointment::factory()->count(5)->cancelled()->create()`. The closure receives the attributes computed so far and, when built through `has()`, the parent model. `sequence(['status' => 'booked'], ['status' => 'no_show'])` wraps a `Sequence` whose values are used round-robin, record by record; a closure value receives the `Sequence`, whose `$index` counts records so far. `forEachSequence()` also sets the count to the number of values. Attributes passed to `create()` or `make()` are applied last, so they override every state. Soft-deletable models get a built-in `trashed()` state.
code
php · 24 lines<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
class AppointmentFactory extends Factory
{
public function definition(): array
{
return [
'starts_at' => fake()->dateTimeBetween('now', '+1 month'),
'status' => 'booked',
];
}
public function cancelled(): static
{
return $this->state(fn (array $attributes) => [
'status' => 'cancelled',
'cancelled_at' => now(),
]);
}
}go deeper
Recall that state() overrides definition(), that a named state is a method returning $this->state(...), and that sequence() alternates values.
Explain the merge order: definition, for() keys, chained states, then call-site attributes, and how Sequence cycles with its index.
Show that you keep definition() valid and minimal and move variants into composable named states, so seeders stay readable as the model grows.
Weigh how many named states a shared factory should carry against scenario-specific overrides, so the factory stays a vocabulary rather than a dumping ground.
## Defaults, then layers A Laravel **model factory** starts from `definition()`, which returns the model's default attributes, typically generated with `fake()` so each record differs. A **state** is a transformation layered on top of those defaults. Internally the factory keeps an ordered list of states and reduces them over the definition with `array_merge`, so: - each state sees the attributes produced so far; - a later state overrides an earlier one for the same key; - attributes passed to `make([...])` or `create([...])` are added as the final state, so they win over everything. Because each factory call returns a new factory instance, states compose freely: `Appointment::factory()->cancelled()->virtual()` applies both. ## Named states A **named state** is a public method on the factory that returns `$this->state(...)`. The skeleton's `UserFactory::unverified()` is the canonical example. For a hospital-appointments app: 1. Add `public function cancelled(): static` to `AppointmentFactory`. 2. Return `$this->state(fn (array $attributes) => ['status' => 'cancelled', 'cancelled_at' => now()])`. 3. Call it anywhere: `Appointment::factory()->count(20)->cancelled()->create()`. A state closure receives two arguments: the attribute array so far and, when the factory is running as a child through `has()`, the parent model. That makes derived values easy, such as setting `ends_at` from the `starts_at` the definition chose. A named state can also chain `afterMaking()` or `afterCreating()` callbacks that apply only when that state is used. Soft-deletable models get a built-in **`trashed()`** state that fills the deleted-at column, with no method to write. ## Sequences A **sequence** is a state that returns a different value for each record. `sequence(...$values)` wraps them in `Illuminate\Database\Eloquent\Factories\Sequence` and adds it as a state. Each time a record is built, the sequence returns the value at `index % count` and then increments `index`: - `count(6)` with three statuses yields each status twice, cycling in order; - a value can be a closure, called with the `Sequence` instance, so `fn (Sequence $s) => ['slot' => $s->index + 1]` numbers records; - `forEachSequence(...)` adds the sequence and sets the count to the number of values, producing exactly one record per value. | Tool | Changes | Typical use | |---|---|---| | `state([...])` | fixed values for every record | one-off override | | named state method | a reusable, named override | `cancelled()`, `unverified()` | | `sequence(...)` | values cycling per record | alternating statuses or slots | | `forEachSequence(...)` | cycling values and the count | one record per listed value | | `create([...])` arguments | final override | pin one attribute in a call | ## How the pieces combine The evaluation order for one record is: `definition()`, then any parent keys from `for()`, then each state in the order it was chained, then the attributes passed to the terminal call. Nested factories and closures in the result are resolved after this merge. So `Appointment::factory()->cancelled()->create(['status' => 'booked'])` stores `booked`: the call-site attributes come last. ## Writing definition() well States only stay simple when the base is sound. A good `definition()`: - returns a **valid** record on its own, so `Appointment::factory()->create()` works with no states; - calls `fake()` inside the returned array, so each record gets fresh values; `fake()` returns a shared Faker generator per locale, and the locale comes from `config('app.faker_locale')`, set by `APP_FAKER_LOCALE` and defaulting to `en_US`; - avoids expensive or surprising work, such as creating many related rows, because every record pays for it; - uses a closure for a value derived from another attribute, for example an `ends_at` computed from `starts_at`. Anything that describes a *variant* (cancelled, virtual, overdue, soft-deleted) belongs in a named state rather than in the defaults. The skeleton's `UserFactory` follows this split: verified users by default, `unverified()` as the variant. ## Why interviewers ask States separate *what a valid record looks like* from *which variant a scenario needs*. A good answer shows that you keep `definition()` minimal and valid, push variants into named states rather than copy-pasting arrays, and use sequences when a realistic dataset needs a spread of values instead of one repeated value.
- What is the difference between sequence() and forEachSequence()?`sequence()` only adds the cycling state; the number of records still comes from `count()`, so values repeat or are cut short. `forEachSequence()` adds the same state and sets the count to the number of values, so each listed value produces exactly one record.
- Can a named state register a callback that runs only for that variant?Yes. Chain `afterMaking()` or `afterCreating()` onto the `$this->state(...)` call inside the state method. The callback is part of the returned factory, so it runs only when that state is used, for example creating a cancellation note after a cancelled appointment is saved.
saying these in an interview costs you the question
- Named states override attributes passed to create()
- sequence() uses only its first value unless count matches
- A state method should mutate $this and return nothing
- definition() is evaluated once and shared by every record
- trashed() must be written by hand on each factory