spatie/laravel-model-states
Add robust state behavior to Laravel Eloquent models using the state pattern and state machines. Represent each state as a class, cast states transparently to/from the database, and define clear, safe transitions with configurable state logic.
Transitions can be used to transition the state of a model from one to another, in a structured and safe way.
You can specify which states are allowed to transition from one to another, and if you want to handle side effects or have more complex transitions, you can also provide custom transition classes.
Transitions are configured in the config method on your state classes.
abstract class PaymentState extends State
{
// …
public static function config(): StateConfig
{
return parent::config()
->allowTransition(Pending::class, Paid::class)
->allowTransition(Pending::class, Failed::class, PendingToFailed::class);
}
}
In this example we're using both a simple transition, and a custom one. You can also allow all transitions for all registered states. Concrete states extending the abstract state class that are located in the same directory as the abstract state class will be automatically registered:
abstract class PaymentState extends State
{
// …
public static function config(): StateConfig
{
return parent::config()
->allowAllTransitions();
}
}
Transitions can then be used like so:
$payment->state->transitionTo(Paid::class);
This line will only work when a valid transition was configured. If the initial state of $payment already was Paid, a \Spatie\ModelStates\Exceptions\TransitionNotFound will be thrown instead of changing the state.
In some cases you may want to handle transition to same state without manually setting allowTransition, you can call ignoreSameState
Please note that the StateChanged event will fire anyway.
abstract class PaymentState extends State
{
// …
public static function config(): StateConfig
{
return parent::config()
->ignoreSameState()
->allowTransition([Created::class, Pending::class], Failed::class, ToFailed::class);
}
}
It also works with IgnoreSameState Attribute
#[IgnoreSameState]
abstract class PaymentState extends State
{
//...
}
A little shorthand allowTransitions can be used to allow multiple transitions at once:
abstract class PaymentState extends State
{
// …
public static function config(): StateConfig
{
return parent::config()
->allowTransitions([
[Pending::class, Paid::class],
[Pending::class, Failed::class, PendingToFailed::class],
]);
}
}
If you've got multiple states that can transition to the same state, you can define all of them in one allowTransition call:
abstract class PaymentState extends State
{
// …
public static function config(): StateConfig
{
return parent::config()
->allowTransition([Created::class, Pending::class], Failed::class, ToFailed::class);
}
}
Transitions can be used by calling the transitionTo method on the state field like so:
$payment->state->transitionTo(Paid::class);
How can I help you explore Laravel packages today?