Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Model States Laravel Package

spatie/laravel-model-states

Add state and state machine behavior to Laravel Eloquent models. Represent each state as a class, automatically serialize to/from the database, and perform clean, explicit transitions with configurable rules—ideal for workflows like payments, orders, and approvals.

View on GitHub
Deep Wiki
Context7

title: Custom transition classes weight: 2

If you want your transitions to do more than just changing the state, you can use transition classes.

Imagine transitioning a payment's state from pending to failed, which will also save an error message to the database. Here's what such a basic transition class might look like.

use Spatie\ModelStates\Transition;

class PendingToFailed extends Transition
{
    private Payment $payment;

    private string $message;

    public function __construct(Payment $payment, string $message)
    {
        $this->payment = $payment;

        $this->message = $message;
    }

    public function handle(): Payment
    {
        $this->payment->state = new Failed($this->payment);
        $this->payment->failed_at = now();
        $this->payment->error_message = $this->message;

        $this->payment->save();

        return $this->payment;
    }
}

Now the transition should be configured in the model:

abstract class PaymentState extends State
{
    // …

    public static function config(): StateConfig
    {
        return parent::config()
            ->allowTransition(Pending::class, Failed::class, PendingToFailed::class);
    }
}

It can be used like so:

$payment->state->transitionTo(Failed::class, 'error message');

Note: the State::transitionTo method will take as many additional arguments as you'd like, these arguments will be passed to the transition's constructor. The first argument in the transition's constructor will always be the model that the transition is performed on.

Another way of handling transitions is by working directly with the transition classes – this allows for better IDE autocompletion, which can be useful to some people. Instead of using transitionTo(), you can use the transition() and pass it a transition class directly.

$payment->state->transition(new CreatedToFailed($payment, 'error message'));

If you're using the approach above, and want to ensure that this transition can only be performed when the payment is in the Created state, you may implement the canTransition() method on the transition class itself.

class CreatedToFailed extends Transition
{
    // …

    public function canTransition(): bool
    {
        return $this->payment->state->equals(Created::class);
    
    }
}

If the check in canTransition() fails, a \Spatie\ModelStates\Exceptions\TransitionNotAllowed will be thrown.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope
anil/file-picker
broqit/fields-ai