PHPackages                             dive-be/laravel-stateful - 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. dive-be/laravel-stateful

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

dive-be/laravel-stateful
========================

State pattern for any object in Laravel

0.6(4y ago)23.4kMITPHPPHP ^8.1

Since Apr 16Pushed 4y ago1 watchersCompare

[ Source](https://github.com/dive-be/laravel-stateful)[ Packagist](https://packagist.org/packages/dive-be/laravel-stateful)[ Docs](https://github.com/dive-be/laravel-stateful)[ RSS](/packages/dive-be-laravel-stateful/feed)WikiDiscussions master Synced today

READMEChangelog (8)Dependencies (7)Versions (9)Used By (0)

State pattern for any object in Laravel
=======================================

[](#state-pattern-for-any-object-in-laravel)

[![Latest Version on Packagist](https://camo.githubusercontent.com/114d8508d13156771442dd5fde4500e830771687ab9e59a6c91830b9845fb593/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f646976652d62652f6c61726176656c2d737461746566756c2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/dive-be/laravel-stateful)

This package adds state support to non-Eloquent objects in Laravel.

If you need support for Eloquent models, there are excellent alternatives:

- [Eloquent State Machines](https://github.com/asantibanez/laravel-eloquent-state-machines)
- [Model States](https://github.com/spatie/laravel-model-states)

⚠️ Minor releases of this package may cause breaking changes as it has no stable release yet.

What problem does this package solve?
-------------------------------------

[](#what-problem-does-this-package-solve)

Usually, when defining "states" or "statuses" on objects, enumeration-like values/objects are used to represent the current state of the object.

While this approach is pragmatic and good enough for simple use cases, it tends to become a mess rapidly when complex domain logic has to be incorporated.

This package solves this problem by using the **state pattern**and the concept of **state machines**.

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

[](#installation)

You can install the package via composer:

```
composer require dive-be/laravel-stateful
```

Usage
-----

[](#usage)

The best example is a practical example.

### Context

[](#context)

Let's say there is a `CheckoutWizard` class that has many possible states: `AddressSelect`, `ShippingSelect`, `Summary`, `Complete`. Each of these states should map to the current index of the Wizard in the front-end, and each step has an associated view that will be resolved by Blade.

[![](https://raw.githubusercontent.com/dive-be/laravel-stateful/master/img/viz.png)](https://raw.githubusercontent.com/dive-be/laravel-stateful/master/img/viz.png)

### Abstract state

[](#abstract-state)

That's why first, we should create an abstract `CheckoutState` class and define all possible transitions, as well as the requirements for each state (which view would we like to show, and what is the index of this state?).

It's important to think carefully about the possible transitions: it makes absolutely zero sense to go back to an `AddressSelect` state if the `CheckoutWizard` has reached the `Complete` state. However, it is perfectly fine for the user to go back to a previous step as long as the `Complete` state is not reached (not shown below, though).

Here's what an abstract version of `CheckoutState` might look like:

```
abstract class CheckoutState extends State
{
    /* You can define which methods need to be implemented for each particular state.
     * For example, you might want each state to have an associated view to resolve. */
    abstract public function view(): string;

    /* Like `view()`, each state should also have an associated index.
     * We'll enforce that this method needs to be implemented by each class
     * that inherits from this one. */
    abstract public function step(): int;

    /* Set up which state transitions are allowed.
     * This is required for the states to function as expected. */
    public static function config(): Config
    {
        return parent::config()
            ->allowTransition(AddressSelect::class, ShippingSelect::class)
            ->allowTransition(ShippingSelect::class, Summary::class)
            ->allowTransition(Summary::class, Complete::class);
    }
}
```

Here's what the `AddressSelect` state might look like, which is extending the abstract `CheckoutState`:

```
class AddressSelect extends CheckoutState
{
    public function view(): string
    {
        return 'checkout.address_select';
    }

    public function step(): int
    {
        return 0;
    }
}
```

### The stateful class

[](#the-stateful-class)

Your stateful classes (classes that will make use of states and possible transitions) should implement the `Stateful` contract and use the `InteractsWithState` trait. (The latter is optional, but recommended. Adhering to the contract is sufficient.)

Also, do not forget to define the initial state in the constructor.

```
class CheckoutWizard implements Stateful
{
    use InteractsWithState;

    public function __construct()
    {
        $this->state = AddressSelect::make($this);
    }

    // Your code here
}
```

### Transitioning

[](#transitioning)

Let's continue from our `CheckoutWizard` example. To transition to another state, you can do the following:

```
$checkout = new CheckoutWizard();

$checkout->getState()->transitionTo(Complete::class);
```

Unless the transition is allowed, a `TransitionFailedException` will be thrown preventing impossible state transitions.

### "Custom" transitions

[](#custom-transitions)

You may define your own `Transition` classes that will have access to the `guard` &amp; `after`/`before` hooks. Passing the FQCN to `allowTransition` in the config will suffice.

```
class AdvanceToShipping extends Transition
{
    public function from(): string { return AddressSelect::class; }

    public function to(): string { return ShippingSelect::class; }
}
```

```
public static function config(): Config
{
    return parent::config()->allowTransition(AdvanceToShipping::class);
}
```

Note: when registering a transition without a custom class, a `DefaultTransition` is created for you automatically.

### Guards (validation)

[](#guards-validation)

Sometimes, even when a specific transition is allowed, certain conditions may have to be met in order to transition. In this case, guards may be used to achieve this behavior. A custom `Transition` is compulsory to use this feature.

- Returning `true` from the guard will allow the transition to take place.
- Returning `false` will cause a `TransitionFailedException` to be thrown.

```
class AdvanceToShipping extends Transition
{
    // omitted for brevity

    public function guard(CheckoutWizard $wizard, MyService $service)
    {
        return $service->isValid($wizard);
    }
}
```

You can access the stateful object by defining a type-hinted method argument with either the `Stateful` contract or your own sub class. Any other type-hinted dependency will be injected via the container using method injection.

Now, every time a transition is attempted, the guard will be executed first.

### Side effects / hooks

[](#side-effects--hooks)

You can use the `after` or `before` hooks on the `Transition` class to define methods that should be executed before or after a certain transition has taken place.

```
class AdvanceToShipping extends Transition
{
    // omitted for brevity

    public function after(CheckoutWizard $wizard)
    {
        // run immediately after the transition
    }

    public function before(CheckoutWizard $wizard)
    {
        // run just before the transition
    }
}
```

Like any guard, hooks can access the stateful object by properly type-hinting a method argument.

Testing
-------

[](#testing)

```
composer test
```

Changelog
---------

[](#changelog)

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

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](CONTRIBUTING.md) for details.

Security
--------

[](#security)

If you discover any security related issues, please email  instead of using the issue tracker.

Credits
-------

[](#credits)

- [Muhammed Sari](https://github.com/mabdullahsari)
- [All Contributors](../../contributors)
- Spatie for their superb [Model States](https://github.com/spatie/laravel-model-states) package that has heavily inspired this package

License
-------

[](#license)

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

###  Health Score

28

—

LowBetter than 54% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity19

Limited adoption so far

Community4

Small or concentrated contributor base

Maturity56

Maturing project, gaining track record

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 ~43 days

Recently: every ~74 days

Total

8

Last Release

1550d ago

PHP version history (2 changes)0.1.0PHP ^8.0

0.6PHP ^8.1

### Community

Maintainers

![](https://www.gravatar.com/avatar/a265853c8db7c699f64f523ee782dc5ec095bfb456379222d1b0b97d90e4c735?d=identicon)[dive](/maintainers/dive)

---

Tags

laravelstatepatterndivetransitionseffectsguards

###  Code Quality

TestsPest

Code StylePHP CS Fixer

### Embed Badge

![Health badge](/badges/dive-be-laravel-stateful/health.svg)

```
[![Health](https://phpackages.com/badges/dive-be-laravel-stateful/health.svg)](https://phpackages.com/packages/dive-be-laravel-stateful)
```

###  Alternatives

[sebdesign/laravel-state-machine

Winzou State Machine service provider for Laravel

3401.3M1](/packages/sebdesign-laravel-state-machine)[dougsisk/laravel-country-state

Country &amp; state helper for Laravel.

1681.7M](/packages/dougsisk-laravel-country-state)[getsolaris/laravel-make-service

A MVCS pattern create a service command for Laravel 5+

81161.3k](/packages/getsolaris-laravel-make-service)[dipeshsukhia/laravel-country-state-city-data

Country State City Data Provider

8230.8k](/packages/dipeshsukhia-laravel-country-state-city-data)[dive-be/laravel-geo

Translate IP addresses into geo locations

3710.5k](/packages/dive-be-laravel-geo)[interaction-design-foundation/laravel-geoip

Support for multiple Geographical Location services.

17221.0k3](/packages/interaction-design-foundation-laravel-geoip)

PHPackages © 2026

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