Git Product home page Git Product logo

laravel-statable's Introduction

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.

laravel-statable's People

Contributors

axklim avatar dependabot[bot] avatar iben12 avatar leonelngande avatar martindilling avatar skullbock avatar vpratfr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

laravel-statable's Issues

Laravel 7 support

Looks like its just adding a composer bump to sebdesign/laravel-state-machine to v3.0

Your recommendations to store comments

Hello,
I would like to test this package but I don't see an option for comments during transitions.
What is your opinion/recommendation if I want to allow users to leave a comment during a transition and to be able to find this comment in the history

Thanks for your help

Invalid argument supplied for foreach()

Hi, thanks for the guide. But I ran into a problem. I did everything according to your documentation, but I always get an error Invalid argument supplied for foreach() in part .../SM/Factory/AbstractFactory.php
I get null when dd($this->configs)
my config file:


return [
    'status' => [
        'class' => App\Project::class,
        'graph' => 'status',
        'property_path' => 'status_id',
        'states' => [
            1, 2, 3, 4, 5, 6
        ],
        'transitions' => [
            'created' => [
                'from' => [1],
                'to' => 2,
            ],
            'success' => [
                'from' =>  [2],
                'to' => 3,
            ],
            'fail' => [
                'from' => [2],
                'to' => 4,
            ],
            'remove' => [
                'from' => [3, 4],
                'to' =>  5,
            ],
            'close' => [
                'from' => [3, 4],
                'to' =>  6,
            ],
        ],
        
        'callbacks' => [
            'history' => [
                'do' => 'StateHistoryManager@storeHistory'
            ]
        ],
    ],
];

I'm trying to call it:

use Statable;
    $project = Project::find(1);
    try {
            $project->apply('close');
        } catch (SMException $e) {
            abort(500, $e->getMessage()); 
        }

Why does this happen?

Not logging state changes

I've setup the model and config as per requirements, i've run migration (and the table is there!) but i can't get the state changes to be logged - would it be that i'm using tinker?
Is it correct that I apply(), then save()?


return [
    'issue' => [
        // class of your domain object
        'class' => App\Models\Issue::class,

        // name of the graph (default is "default")
        'graph' => 'issue',

        // property of your object holding the actual state (default is "state")
        'property_path' => 'state',

        // list of all possible states
        'states' => [
            'submitted',
            'logged',
            'team_assigned',
            'user_assigned',
            'in_progress',
            'closed',
        ],

        // list of all possible transitions
        'transitions' => [
            'log_issue' => [
                'from' => ['submitted'],
                'to' => 'logged',
            ],
            'assign_team' => [
                'from' => ['logged'],
                'to' => 'team_assigned',
            ],
            'change_team' => [
                'from' => ['user_assigned', 'in_progress'],
                'to' => 'team_assigned',
            ],
            'change_user' => [
                'from' => ['in_progress'],
                'to' => 'user_assigned',
            ],
            'assign_user' => [
                'from' => ['team_assigned', 'logged'],
                'to' => 'user_assigned',
            ],
            'accept_job' => [
                'from' => ['user_assigned'],
                'to' => 'in_progress',
            ],
            'close' => [
                'from' =>  ['submitted', 'in_progress', 'logged'],
                'to' => 'closed',
            ],
        ],

        // list of all callbacks
        'callbacks' => [
            // will be called when testing a transition
            'guard' => [
                'guard_on_submitting' => [
                    // call the callback on a specific transition
                    'on' => 'submit_changes',
                    // will call the method of this class
                    'do' => ['MyClass', 'handle'],
                    // arguments for the callback
                    'args' => ['object'],
                ],
                'guard_on_approving' => [
                    // call the callback on a specific transition
                    'on' => 'approve',
                    // will check the ability on the gate or the class policy
                    'can' => 'approve',
                ],
            ],
            'history' => [
                'do' => ['StateHistoryManager','storeHistory']
            ],

            // will be called before applying a transition
            'before' => [],

            // will be called after applying a transition
            'after' => [],
        ],
    ],
];
namespace App\Models;

use Iben\Statable\Statable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Collection;

class Issue extends Model

    use SoftDeletes, Statable;

    /*
    |--------------------------------------------------------------------------
    | GLOBAL VARIABLES
    |--------------------------------------------------------------------------
    */
    protected $table = 'issues';
    protected $primaryKey = 'id';
    public $timestamps = true;
    // protected $guarded = ['id'];
    protected $fillable = [
        'event_id',
        'reporting_user_id',
        'description',
        'location_lat',
        'location_lng',
        'state'
    ];

    /*
    |--------------------------------------------------------------------------
    | FUNCTIONS
    |--------------------------------------------------------------------------
    */
    protected function getGraph()
    {
        return 'issue'; // the SM config to use
    }

}

multiple graphs/property_paths on same class

Hi, I was wondering if it would be possible to apply more than one statemachine to the class? e.g. when you have two different state variables on the same model?
It seems that right now, getGraph can only set for one graph that in turn is coupled to one property_path.

Allow passing of context array in `apply` and `can` methods.

Hi ๐Ÿ‘‹,

Decided to create an issue before moving on with a pull request for this.
"sebdesign/laravel-state-machine": "^2.1" introduces the ability to pass a context array to stateMachine@apply and stateMachine@can, documented here.

I've checked and this package is still using "sebdesign/laravel-state-machine": "^2.0", and support for the above feature is missing.

Is it ok if I go ahead and update to version 2.1 and update the Statable trait methods apply and canApply to accept a context array? e.g apply('to_reviewed', false, ['foo' => 'bar']).

Custom StateHistory model or connection

We are using the package in a multi-tenant app. The connection for the models is determined dynamically and thus the StateHistory model needs to be set on the proper connection.

I see two options:

  1. allow configuring a custom model for the StateHistory model used (to provide a custom class which will use the right connection).
  2. have a configuration option to set the connection to be used by the models of the package. That setting would then be used by the StateHistory model

I would suggest 1., this is the way most packages allow such customization.

Would you be able to do that? Do you want a PR?

Update for sebdesign/laravel-state-machine v2

I'd like to give this package a try but it does not support the newest stable versions of sebdesign/laravel-state-machine. It was recently updated to support Laravel 6 and bumped its versions to 2.x.

See its changelog for more details.

Unclear to me if this would be as easy as relaxing the version constraints for sebdesign/laravel-state-machine or if it would require more work.

Add a variation of saveBeforeTransition() for automatically persisting last_state in database?

Hi again,

You created this trait to make it easier to work with the state machine, correct? :-)
A pain point I've been dealing with is having to manually save the model after successfully applying a transition. That is so the updated last_state value is persisted in the database.

What if this could equally happen in Statable@apply() method? A variation of Statable@saveBeforeTransition() that takes care of this use case.

What's your thought on this please? An implementation that won't break the current saveBeforeTransition() functionality.

Thanks ๐Ÿ™.

Laravel 8 Support

Hello, I recently upgraded to Laravel 8 (using Shift) and as soon as I try to use this package it no longer works. I dont get any errors oddly but if I try to transition a state it will fail saying that its not allowed. The only changes is the framework upgrade. I tried this on a fresh Laravel 8 install and the same issue was there. It also seems to be ignoring the guards which is probably the core issue here. Any ideas?

Also here: sebdesign/laravel-state-machine#54

v1.0 support for laravel 5.5+

I saw that the sebdesign/laravel-state-machine v2 supports laravel 5.5+.

If you dont plan on using breaking changes from laravel 6, can you update the document to say 5.5+ support for v1.0?

PHP 8 Support

Hi,
I'm enjoying using this package, and as I test a migration to PHP 8 I'm getting a warning from composer that this package isn't compatible. My tests using the trait have no complaints with the shift FYI.

  • iben12/laravel-statable[v1.4, ..., v1.4.1] require php ^7.2 -> your php version (8.0; overridden via config.platform, same as actual) does not satisfy that requirement.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.