/finite

Simple yet powerful Finite State Machine implementation

Primary LanguagePHP

WORK IN PROGRESS


Usage

$order = new Order(); // see below
$graph = new Graph(); // see below

$graph->apply($order, 'transition-input');
use SegfaultInc\Finite\Subject;

class Order extends Model implements Subject
{
    public function setFiniteState($state)
    {
        $this->update([
            'status' => $state,
        ]);
    }

    public function getFiniteState($state)
    {
        return $this->status;
    }
}

Graph definition

(new Graph)
    ->setStates([

        State::initial('new')
            ->label('New')
            ->leaving(function (Order $order) {
                // This is executed every time the `in_progress` is **transitioned from**.
                // Useful if you want to do something that is "state" dependent

                $order->update([
                    'started_at' => now()
                ]);
            }),

        State::normal('in_progress')
            ->label('In Progress'),

        State::final('finished')
            ->label('Finised'),

        State::final('canceled')
            ->label('Canceled')
            ->entering(function (Order $order) {
                // If you transition to "canceled" state from multiple states,
                // this is the place to perform the common logic
            }),

    ])
    ->setTransitions([

        Transition::make('new', 'in_progress', 'progress')
            ->pre(function (Order $order) {
                if ($order->validate()) {
                    throw new OrderValidationException('When an exception is thrown in "pre" hook, the transition is not applied');
                }
            }),

        Transition::make('in_progress', 'finished', 'progress'),

        Transition::make('in_progress', 'canceled', 'cancel')
            ->post(function (Order $order) {
                $order->user->notify(new YourOrderWasCanceled);
            }),

    ]);

Visualization

$graph->visualize()

// uses GraphViz under the hood