- How do I install spatie/laravel-model-states in a Laravel app?
- Install it with Composer using composer require spatie/laravel-model-states. Optionally publish the config file, which contains a single default_transition key. Add the HasStates trait to your Eloquent model and cast a string column to your abstract state class, then create one concrete class per state.
- How do I define allowed transitions between states?
- Declare the transition graph declaratively in your abstract state class using StateConfig::config() with allowTransition(), or use PHP 8 attributes such as AllowTransition, DefaultState, and RegisterState. The legal state space then lives in code and is reviewable, testable, and diffable. Attempting a transition that is not allowed throws an exception.
- Does spatie/laravel-model-states support multiple state fields on one model?
- Yes. A model can have multiple independent state fields, each with its own string column, Eloquent cast, and state class hierarchy. This is useful for real order lifecycles where fields like state and fulfillment move independently. Each state field gets its own transition rules and state classes.
- How are states stored in the database with spatie/laravel-model-states?
- States are serialized automatically to a string column as class names by default, for example App\States\Payment\Pending. Each state class can declare public static $name to control the stored value. Use $name before renaming or moving state classes, because class-name persistence will otherwise silently corrupt historical rows.
- What Laravel and PHP versions does spatie/laravel-model-states require?
- The package requires Laravel with Eloquent and PHP 8 or higher if you want to use attribute-based configuration. The StateConfig fluent API works on older PHP versions. Check the Composer constraints on Packagist for the exact Laravel and PHP versions supported by your chosen release.
- How do I run side effects when a state transition happens?
- Use the StateChanged event, which can be overridden and gives you a clean hook for side effects, audit logging, and async fan-out. Queue your listeners if they perform slow or failure-prone work. Otherwise listener failures will surface inside the transition call path.
- Is spatie/laravel-model-states safe for concurrent payment transitions?
- Transition validation happens in-process, and the package does not ship optimistic locking or database-level guards. Two concurrent requests can both read Pending, both validate Pending to Paid, and both write. You must add row locking or database constraints around transitions if correctness under concurrency matters, especially in payment flows.
- Can I use spatie/laravel-model-states with native PHP enums?
- Native PHP enum casts and this package's custom State cast are mutually exclusive on the same column. You must choose one approach per state field. The package uses its own State cast and class hierarchy rather than native enum casts.
- How do I test state transitions with spatie/laravel-model-states?
- Because each state is a class and the transition graph is declared in code, tests can assert that allowed transitions succeed and disallowed ones throw. Create a model in a known state, call transitionTo, and assert the persisted state value. You can also assert that the StateChanged event was dispatched for side-effect coverage.
- What is the difference between spatie/laravel-model-states and a simple status column?
- A simple status column is fine when status is only a reporting label with no conditional logic. This package adds a class per state, state-specific behaviour, and a declarative transition graph that is reviewable and testable. The class-per-state overhead buys little for models without behavioural state.