PHPackages                             bvtterfly/model-state-machine - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. bvtterfly/model-state-machine

AbandonedArchivedLibrary[Utility &amp; Helpers](/categories/utility)

bvtterfly/model-state-machine
=============================

Laravel Model State Machine

0.3.0(3y ago)2125.6k↓50%6[2 PRs](https://github.com/bvtterfly/model-state-machine/pulls)MITPHPPHP ^8.1

Since Apr 23Pushed 2y ago1 watchersCompare

[ Source](https://github.com/bvtterfly/model-state-machine)[ Packagist](https://packagist.org/packages/bvtterfly/model-state-machine)[ Docs](https://github.com/bvtterfly/model-state-machine)[ RSS](/packages/bvtterfly-model-state-machine/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (3)Dependencies (11)Versions (6)Used By (0)

🚨 THIS PACKAGE HAS BEEN ABANDONED 🚨

I no longer use Laravel and cannot justify the time needed to maintain this package. That's why I have chosen to abandon it. Feel free to fork my code and maintain your own copy.

Laravel Model State Machine
===========================

[](#laravel-model-state-machine)

[![Latest Version on Packagist](https://camo.githubusercontent.com/8a57c3c64d90406f1ff193f1dae2da15f97a2a6d475db1da0abd09d8841668bc/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f627674746572666c792f6d6f64656c2d73746174652d6d616368696e652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/bvtterfly/model-state-machine)[![run-tests](https://github.com/bvtterfly/model-state-machine/actions/workflows/run-tests.yml/badge.svg)](https://github.com/bvtterfly/model-state-machine/actions/workflows/run-tests.yml)[![Check & fix styling](https://github.com/bvtterfly/model-state-machine/actions/workflows/php-cs-fixer.yml/badge.svg)](https://github.com/bvtterfly/model-state-machine/actions/workflows/php-cs-fixer.yml)[![Total Downloads](https://camo.githubusercontent.com/51457f193ce1191b3dab278890ea6bea9cab065c99c88e5b47fd8a540b532d3b/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f627674746572666c792f6d6f64656c2d73746174652d6d616368696e652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/bvtterfly/model-state-machine)

This package adds support for creating state machines for attributes on Laravel Eloquent Models.

Requirements
------------

[](#requirements)

- PHP 8.1 or higher
- Laravel 9.x or higher

Installation
------------

[](#installation)

You can install the package via composer:

```
composer require bvtterfly/model-state-machine
```

Usage
-----

[](#usage)

For example, we have a blog system, and our blog posts have three statuses: draft, pending, and published. When we are writing a post, it's in the draft status. Whenever we finish writing our blog posts, we schedule them for publication in the future, which changes the status of the post to pending. Once the post is published, the status changes to published.

The simplest backed enum that holds the states of a blog post status is:

```
use Bvtterfly\ModelStateMachine\Attributes\AllowTransitionTo;
use Bvtterfly\ModelStateMachine\Attributes\InitialState;

enum PostState: string
{
    #[InitialState]
    #[AllowTransitionTo(self::PENDING)]
    case DRAFT = 'draft';

    #[AllowTransitionTo(self::PUBLISHED)]
    case PENDING = 'pending';

    case PUBLISHED = 'published';

}
```

Here's what the Blog post model would look like:

```
class Post extends Model
{
    use HasStateMachine;

    protected $casts = [
      'status' => PostState::class
    ];

    public function getStateMachineFields(): array
    {
        return [
            'status'
        ];
    }

}
```

A model can have as many state machine fields as you want, You need to add them to the list using the `getStateMachineFields` method.

> Since State machine loads state configuration from string backed enums, You need to cast state machine fields to correlated state enums in your model.

Now, You can get your state machine:

```
$stateMachine = $post->getStateMachine('status')
```

### Get All states

[](#get-all-states)

You can use the `getAllStates` method, which return collection of the all available states:

```
$stateMachine->getAllStates();
```

### Get All allowed transitions

[](#get-all-allowed-transitions)

You can use the `getStateTransitions` method, which return collection of available transitions for current/initial state

```
$stateMachine->getStateTransitions();
```

> If the state field is null and the state configuration doesn't have a initial state (field in unknown state), It will throw an exception.

If you want to get available transitions for a state, You can pass it to the method:

```
$stateMachine->getStateTransitions(PostState::PENDING);
// or $stateMachine->getStateTransitions('pending');
```

### Using transitions

[](#using-transitions)

To use transitions, call the `transitionTo` method on the state field as follows:

```
$stateMachine->transitionTo(PostState::PUBLISHED);
// or $stateMachine->transitionTo('published');
```

> You can pass array as a second argument to the `transitionTo` method for additional data that you'll need in your actions and transitions.

### State Actions

[](#state-actions)

You can add actions to run if a state changes to a state. In the above example, Maybe we want to send a tweet and send email to subscribers when the post is published.

We can do this using the `#[Actions]` attribute. Here's how our `PostState` would look like:

```
enum PostState: string
{
    #[InitialState]
    #[AllowTransitionTo(self::PENDING)]
    case DRAFT = 'draft';

    #[AllowTransitionTo(self::PUBLISHED)]
    case PENDING = 'pending';

    #[Actions(SendTweetAction::class, SendEmailToSubscribers::class)]
    case PUBLISHED = 'published';

}
```

Actions are classes that implements `Bvtterfly\ModelStateMachine\Contracts\StateMachineAction`:

```
class SendTweetAction implements StateMachineAction
{

    public function handle(Model $model, array $additionalData): void
    {
        // send tweet...
    }
}
```

> Your actions may also type-hint any dependencies they need on their constructors. All actions are resolved via the Laravel service container, so dependencies will be injected automatically.

### Transition Actions

[](#transition-actions)

In addition to state actions, maybe you want to run actions only when a specific state transit to another state.

You can pass array of actions as second argument to `#[AllowTransitionTo]`.

In the above example, If we want to send a notification to the admin when the post status change to the pending, Our `PostState` would look like this:

```
enum PostState: string
{
    #[InitialState]
    #[AllowTransitionTo(self::PENDING, [SendNotificationToAdmin::class])]
    case DRAFT = 'draft';

    #[AllowTransitionTo(self::PUBLISHED)]
    case PENDING = 'pending';

    #[Actions(SendTweetAction::class, SendEmailToSubscribers::class)]
    case PUBLISHED = 'published';
}
```

> Transition Actions run **before** State Actions

### Action With Validation

[](#action-with-validation)

When using transitions, you can pass additional data as a second argument, and this data will pass to all actions. So, It's necessary to validate this data before running actions.

Validators are actions that implement `Bvtterfly\ModelStateMachine\Contracts\StateMachineValidation`.

In above example, We want to send notification to admin when post status changes to pending:

```
$stateMachine->transitionTo(PostState::PENDING, [
    'message' => '...'
]);
```

Here's how our `SendNotificationToAdmin` action would look like:

```
class SendNotificationToAdmin implements StateMachineAction, StateMachineValidation
{

        public function validate(Model $model, array $additionalData): void
    {
        $validator = validator($additionalData, [
            'message' => 'required',
        ]);

        if ($validator->fails()) {
            // throw exception
        }
    }

    public function handle(Model $model, array $additionalData): void
    {
        // send notification...
    }
}
```

### Custom transition Classes

[](#custom-transition-classes)

This package comes with a default transition class that save new state after running State &amp; Transition Actions. If you need to do more than just changing to the new state, you can use transition classes.

Custom transition are classes that implements `Bvtterfly\ModelStateMachine\Contracts\StateTransition`.

For example, We want to store the `user_id` of who changes the status of a post to `pending` status in the post model:

```
class DraftToPending implements StateTransition
{
    public function commitTransition(
            BackedEnum|string $newState,
            Model $model,
            string $field,
            array $additionalData
        ): void {
            $model->{$field} = $newState;
            $model->causer = $additionalData['user_id']
            $model->save();
        }
}
```

You can pass this class as a third argument to the `#[AllowTransitionTo]`.

Then, Our `PostState` would look like this:

```
enum PostState: string
{
    #[InitialState]
    #[AllowTransitionTo(self::PENDING, [SendNotificationToAdmin::class], DraftToPending::class)]
    case DRAFT = 'draft';

    #[AllowTransitionTo(self::PUBLISHED)]
    case PENDING = 'pending';

    #[Actions(SendTweetAction::class, SendEmailToSubscribers::class)]
    case PUBLISHED = 'published';
}
```

Testing
-------

[](#testing)

```
composer test
```

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Security Vulnerabilities
------------------------

[](#security-vulnerabilities)

Please review [our security policy](../../security/policy) on how to report security vulnerabilities.

Credits
-------

[](#credits)

- [Ari](https://github.com/bvtterfly)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

34

—

LowBetter than 77% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity38

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity51

Maturing project, gaining track record

 Bus Factor2

2 contributors hold 50%+ of commits

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~158 days

Total

3

Last Release

1170d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/99682351?v=4)[Λгi](/maintainers/bvtterfly)[@bvtterfly](https://github.com/bvtterfly)

---

Top Contributors

[![bvtterfly](https://avatars.githubusercontent.com/u/99682351?v=4)](https://github.com/bvtterfly "bvtterfly (14 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (10 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (9 commits)")

---

Tags

laravelbvtterflymodel-state-machine

###  Code Quality

TestsPest

### Embed Badge

![Health badge](/badges/bvtterfly-model-state-machine/health.svg)

```
[![Health](https://phpackages.com/badges/bvtterfly-model-state-machine/health.svg)](https://phpackages.com/packages/bvtterfly-model-state-machine)
```

###  Alternatives

[spatie/laravel-data

Create unified resources and data transfer objects

1.8k28.9M627](/packages/spatie-laravel-data)[hirethunk/verbs

An event sourcing package that feels nice.

513162.9k6](/packages/hirethunk-verbs)[worksome/exchange

Check Exchange Rates for any currency in Laravel.

123544.7k](/packages/worksome-exchange)[ralphjsmit/livewire-urls

Get the previous and current url in Livewire.

82270.3k4](/packages/ralphjsmit-livewire-urls)[hydrat/filament-table-layout-toggle

Filament plugin adding a toggle button to tables, allowing user to switch between Grid and Table layouts.

6292.3k1](/packages/hydrat-filament-table-layout-toggle)[ralphjsmit/laravel-helpers

A package containing handy helpers for your Laravel-application.

13704.6k2](/packages/ralphjsmit-laravel-helpers)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
