Laravel eloquent状态机

1.5.1 2019-05-02 08:33 UTC

This package is auto-updated.

Last update: 2024-09-29 04:43:18 UTC


README

这是一个用于eloquent模型的Laravel 5.6包。其目的是为模型添加状态机功能

特性

  • 所有状态机在一个配置文件中
  • 状态定义为名称,值
  • 从多个状态进行转换
  • 转换影响相关模型
  • 按照你定义的X次运行一次计划
  • 根据状态和上次状态更改时间进行转换计划
  • 计划支持根据状态运行命令
  • 支持共享特性以共享属性或覆盖{一些}stager方法
  • 支持转换前后的事件
  • 支持访问转换之前额外的执行或额外条件
  • 自动生成ide帮助文件用于魔术函数
  • [新]受保护的转换
  • [新]转换前关系状态检查器
  • [新]支持集合转换

安装

composer require ibraheem-ghazi/stager
php artisan vendor:publish --provider="IbraheemGhazi\Stager\StagerServiceProvider"

配置

  • 为了添加状态机,您必须在config/state-machine.php中定义它

#####注意

  • 每个状态由生成器定义一个常量
  • 每个状态必须具有当前模型的唯一值,以便在获取当前状态名称时不会与其他重叠。
  • state-column和init-state属性是可选的,默认情况下state-column = state,init-state默认为第一个状态
  • 每个属性键必须使用短横线命名法
<?php
/**
 * On each modification happened to this config file you must run artisan command stager:generate to update files and ide helpers
 * NOTE: any key must be in kebab-case only
 */
return [

    'config'=>[
        'ide-helper-path'=>'stager-methods-ide-helper.php',//where to save ide helper file (currently in root)
        'fail-throw-exception'=>true, //useful for debug , for production its better to turn it off
        'schedule-cronjob'=>'0 * * * *', //run cron job every hour once to handle schedule
        'unauthorized-gaurd-exception'=>true,//should throw unauthorized exception if guard requesting this transition is not in guard array , if false then can method only return false without exception

        //generator config
        'constants-prefix'=>'STATE_', //
        'shared-trait'=>[
            // you can use this a trait to add shared functionality to all models that use stager
            // or to override scopeStateChangeWithin , getStateChangedAt functions

          //  \App\Traits\SharedTrait::class,
        ],
    ],


    ////////////////////////////////////////////


    \App\Payment::class => [

        'state-column'=>'state', //(optional, default: state)

        'init-state'=>'pending',//(optional, default: the first defined state

        'states' => [
            //state-name => numeric value
            'pending' =>1,
            'payment-accepted'=>2,
            'in-progress'=>3,
            'ended' => 4,
        ],


        'schedules'=>[

            //state-name
            'pending'=>[

                //if last state change time has passed trigger-delay then it will be included in transition run
                // for example:  run transition for all payments that has state 'pending' and changed from 2 days or more
                'trigger-delay' =>[
                    'time-modifier'=>"DAY",
                    'interval'=>2
                ],

                // the transition to be run on each row apply this schedule requirement (state , last state change time)
                'transition'=>'payment-success',

                // run commands after transition excuted
                'commands'=>[
                    'command:subcommand'=>['param1'=>'val1'],
                ]
            ],
        ],
        'transitions' => [
            'payment-success' => [
                'from' => 'pending',
                'to' => 'payment-accepted',
                'relation-state-condition'=>[
                   //relation class must be defined here in state-machine config
                    'some-realtion'=>'status'
                ],
                'guard'=>['web'],//array of guards or string equal to '*' [default = '*']
                //todo: affection class
                'affect'=>[
                    //relation => transiojn_of_relation
                      'order' => 'waiting-seller'
                ],

            ],
            'seller-accept' => [
                'from' => 'payment-accept',
                'to' => 'in-progress',
            ],
            'finish' => [
                'from' => 'in-progress',
                'to' => 'ended',
            ],
            'canceled' => [
                'from' => 'pending',
                'to' => 'ended',
            ],

        ],
    ],
];
  • 每次更新配置文件时,您都必须运行
$ php artisan stager:generate
-C or --clean clean auto generated code from all registered models 
-M or -model \App\MyModel clean auto generated code from sepcified model

此命令将自动修改实际模型文件,并添加所需的use语句和从状态写入所需的常量

//an example of auto-generated data to model

<?php

namespace App;


use \IbraheemGhazi\Stager\Traits\Stager;
use Illuminate\Database\Eloquent\Model;

class Payment extends Model
{
    /**** AUTO-GENERATED STAGER DATA ****/
    
    use Stager;

    const STATE_PENDING = 1;
    const STATE_PAYMENT_ACCEPTED = 2;
    const STATE_IN_PROGRESS = 3;
    const STATE_ENDED = 4;
    
    /**** END OF AUTO-GENERATED STAGER DATA ****/
}

注意:您必须编辑这些注释(或注释本身)之间的任何内容

/**** AUTO-GENERATED STAGER DATA ****/

...

/**** END OF AUTO-GENERATED STAGER DATA ****/

生成器将读取模型文件,如果它有这些注释,则将用新自动生成的代码替换其内容;否则,如果找不到这些注释,它将假设类不包含该数据

共享特性

共享特性用于为所有使用stager的模型定义共享属性,例如,如果您想添加一个关系,它主要定义了这样,您可以轻松覆盖2个Stager方法以获取最后状态更改时间

getStateChangedAt() 获取状态最后更改的时间

paramters :  -

scopeStateChangeWithin($query, $modifier, $interval) 范围以获取X天内的更改行,例如

paramters :  
    * $query = default query paramter for scope from laravel
    * $modifier = sql modifiter [YEAR, MONTH, DAY, HOUR, MINUTE, SECOND]
    * $interval = integer value define the interval based on modifier

preTransitionNameTransition函数

如果模型中定义了以过渡名的骆驼命名法的函数,并且具有前缀pre和后缀Transition,则将在过渡动作之前执行该函数;如果它返回TRUE,则将完成过渡,否则将终止它

示例:假设您有一个名为:取消订单的过渡

function preCancelOrderTransition(...$args){

}

其中...$args是从doCancelOrder传递的参数数组

可用函数

可用魔术函数

每个魔术函数都有前缀[is, can, do],后面跟着目标状态或过渡的骆驼命名法

isStateName()
* no parameters required
* return is current model row state equal the state name
canTransitionName()
* no parameters required
* return is current model able to move to given transition from current state or not
doTransitionName()
* accept multiple parameters as you required ( these parameters will be pass to preTransitionNameTransition() )
* execute the transition 
* return model or false on failed (note that if `fail-throw-exception` is enabled it will throw an exception instead )

过渡生命周期

  1. 检查是否可以从当前状态进行转换
  2. 如果模型定义了preTransitionNameTransition,则将其执行;如果它返回true,则转换将继续,否则将终止(失败)
  3. 触发beforeTransition事件
  4. 更新状态
  5. 如果存在,执行影响
  6. 触发afterTransition事件