Custom transition classes | laravel-model-states | Spatie

 SPATIE

  Laravel Model States
=======================

spatie.be/open-source

  [Docs](https://spatie.be/docs)  [Laravel-model-states](https://spatie.be/docs/laravel-model-states/v1)  Working-with-transitions  Custom transition classes

 Version   v2   v1

 Other versions for crawler [v2](https://spatie.be/docs/laravel-model-states/v2) [v1](https://spatie.be/docs/laravel-model-states/v1)

- [ Introduction ](https://spatie.be/docs/laravel-model-states/v1/01-introduction)
- [ Postcardware ](https://spatie.be/docs/laravel-model-states/v1/02-postcardware)
- [ Requirements ](https://spatie.be/docs/laravel-model-states/v1/03-requirements)
- [ Installation &amp; setup ](https://spatie.be/docs/laravel-model-states/v1/04-installation-setup)
- [ Questions and issues ](https://spatie.be/docs/laravel-model-states/v1/05-questions-issues)
- [ Changelog ](https://spatie.be/docs/laravel-model-states/v1/06-changelog)
- [ About us ](https://spatie.be/docs/laravel-model-states/v1/07-about-us)

Working with states
-------------------

- [ Configuring states ](https://spatie.be/docs/laravel-model-states/v1/working-with-states/01-configuring-states)
- [ Serializing states ](https://spatie.be/docs/laravel-model-states/v1/working-with-states/02-serializing-states)
- [ Listing states ](https://spatie.be/docs/laravel-model-states/v1/working-with-states/03-listing-states)

Working with transitions
------------------------

- [ Configuring transitions ](https://spatie.be/docs/laravel-model-states/v1/working-with-transitions/01-configuring-transitions)
- [ Custom transition classes ](https://spatie.be/docs/laravel-model-states/v1/working-with-transitions/02-custom-transition-classes)
- [ Dependency injection in transition classes ](https://spatie.be/docs/laravel-model-states/v1/working-with-transitions/03-dependency-injection-in-transition-classes)
- [ Retrieving transitionable states ](https://spatie.be/docs/laravel-model-states/v1/working-with-transitions/04-retrieving-transitionable-states)

Querybuilder support
--------------------

- [ State scopes ](https://spatie.be/docs/laravel-model-states/v1/querybuilder-support/01-state-scopes)

Request validation
------------------

- [ State validation rule ](https://spatie.be/docs/laravel-model-states/v1/request-validation/01-state-validation-rule)

      You are viewing the documentation for **an older version** of this package. You can check the version you are using with the following command:

 `                                    composer show spatie/laravel-model-states                                                                                                                                                                                                                                    `

Custom transition classes
=========================

If you want your transitions to do more stuff 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
{
    /** @var Payment */
    private $payment;

    /** @var string */
    private $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:

```
class Payment extends Model
{
    // …

    protected function registerStates(): void
    {
        $this->addState('state', PaymentState::class)
            ->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->is(Created::class);

        // return $this->payment->state->isOneOf(Created::class, Pending::class);
    }
}
```

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

> **Note**: `transition()` also supports a shorthand: `$payment->state->transition(CreatedToFailed::class, 'message')`.
