Skip to content

Trait for drop-in state management for Eloquent models

License

Notifications You must be signed in to change notification settings

iben12/laravel-statable

Repository files navigation

Statable trait for Laravel Eloquent models

Run tests StyleCI Scrutinizer Code Quality

This trait provides drop-in functionality to manage state and state history of an existing Eloquent Model based on winzou/state-machine using sebdesign/laravel-state-machine service provider.

Installation

Compatibility:

Version Upstream Laravel PHP
v0.1 sebdesign/laravel-state-machine:^1.3 < 5.5
v1.3 sebdesign/laravel-state-machine:^2.0 >= 5.5
v1.4 sebdesign/laravel-state-machine:^3.0 >= 7.0 >= 7.3
v1.5 sebdesign/laravel-state-machine:^3.2 >= 7.0 >= 8.0

So if you are below Laravel 5.5, require 0.1 version explicitly. For Laravel below 7 require version v1.3.

Use composer to pull in the package:

$ composer require iben12/laravel-statable

Publish the database migration and state machine config:

$ php artisan vendor:publish --provider="Iben\Statable\ServiceProvider"

Migrate the database:

$ php artisan migrate

This migration creates the table for storing history of your models as a polymorphic relation.

Usage

Prerequisites

  • Model class with some property holding state (we use last_state in the example)

Setup

For this manual we will use a Post model as example.

First you configure the SM graph. Open config/state-machine.php and define a new graph:

return [
    'post' => [
        'class' => App\Post::class,
        'graph' => 'post',

        'property_path' => 'last_state', // should exist on model

        'states' => [
            'draft',
            'published',
            'archived'
        ],
        'transitions' => [
            'publish' => [
                'from' => ['draft'],
                'to' => 'published'
            ],
            'unpublish' => [
                'from' => ['published'],
                'to' => 'draft'
            ],
            'archive' => [
                'from' => ['draft', 'published'],
                'to' => 'archived'
            ],
            'unarchive' => [
                'from' => ['archived'],
                'to' => 'draft'
            ]
        ],
        'callbacks' => [
            'after' => [
                'history' => [
                    'do' => 'StateHistoryManager@storeHistory'
                ]
            ]
        ]
    ]
]

Now you have to edit the Post model:

namespace App;

use \Illuminate\Database\Eloquent\Model;
use \Iben\Statable\Statable;

class Post extends Model
{
    use Statable;

    protected function getGraph()
    {
    	return 'post'; // the SM config to use
    }
}

And that's it!

Usage

You can now access the following methods on your model:

$post = \App\Post::first();

$post->last_state; // returns current state

try {
    $post->apply('publish'); // applies transition
} catch (\SM\SMException $e) {
    abort(500, $e->getMessage()); // if transition is not allowed, throws exception
}

$post->canApply('publish'); // returns boolean

$post->stateHistory()->get(); // returns PostState collection for the given Post

$post->stateHistory()->where('user_id', \Auth::id())->get(); // you can query history as any Eloquent relation

NOTE: The history saves the currently authenticated user, when applying a transition. This makes sense in most cases, but if you do not use the default Laravel authentication you can override the getActorId method to store the user with the history.

class Post extends Model
{
    // ...

    public function getActorId()
    {
        // return id;
    }
}

If the model is newly created (never been saved), so it does not have an id when applying a transition, history will not be saved. If you want to be sure that all transitions are saved in history, you can add this method to your model:

    protected function saveBeforeTransition()
    {
        return true;
    }

State machine

sebdesign/laravel-state-machine provides a lot of features:

  • using Gates and Policies
  • Events
  • callbacks for guards or other tasks

You can find the documentation in the repo.

If you want to interact directly with the StateMachine object of your model, call $model->stateMachine().

License

The MIT License (MIT). Please see License File for more information.