davidsteinsland/cakephp-state-machine

该软件包已被废弃且不再维护。未建议替代软件包。

CakePHP的StateMachineBehavior

1.1 2013-10-03 12:19 UTC

This package is not auto-updated.

Last update: 2017-07-01 09:31:47 UTC


README

Build Status Coverage Status Scrutinizer Quality Score Latest Stable Version Total Downloads

文档尚未完成。如果您想了解一些内容,请查看测试,因为所有状态机的方面都在那里进行了测试。

什么是状态机?

http://en.wikipedia.org/wiki/State_machine

安装

首先,您需要修改您想要使用StateMachine的模型的表

ALTER TABLE `vehicle` ADD `state` VARCHAR(50);
ALTER TABLE `vehicle` ADD `previous_state` VARCHAR(50);

功能

  • 状态和转换上的回调
  • 可以为您模型添加自定义方法
  • is($state)can($transition)on($transition, 'before|after', callback)when($state, callback) 方法允许您控制整个流程。使用 transition($transition) 在两个状态之间移动。
  • 角色和规则
  • Graphviz

回调

您可以在转换之前/之后、状态更改之前/之后添加回调。这可以通过 $this->on('mytransition', 'before', funtion() {}) 手动完成,或者您可以在模型中添加一个方法

public function onBeforeTransition($currentState, $previousState, $transition) {
    // will fire on all transitions
}

public function onAfterIgnite($currentState, $previousState, $transition) {
    // will fire after the ignite transition
}

状态回调略有不同

public function onStateChange($newState) {
    // will fire on all state changes
}

public function onStateIdling($newState) {
    // will fire on the idling state
}

命名约定

  • $transitions 中的转换和状态应该是 小写下划线。方法名称随后采用驼峰式。

    示例

    shift_up   => canShiftUp() => shiftUp()
    first_gear => isFirstGear()

如何使用

App::uses('StateMachineBehavior', 'StateMachine.Model/Behavior');

class VehicleModel extends AppModel {

	public $useTable = 'Vehicle';

	public $actsAs = array('StateMachine.StateMachine');

	public $initialState = 'parked';

	public $transitionRules = array(
        'ignite' => array(
 			'role' => array('driver'),
			'depends' => 'has_key'
		)
	);

	public $transitions = array(
		'ignite' => array(
			'parked' => 'idling',
			'stalled' => 'stalled'
		),
		'park' => array(
			'idling' => 'parked',
			'first_gear' => 'parked'
		),
		'shift_up' => array(
			'idling' => 'first_gear',
			'first_gear' => 'second_gear',
			'second_gear' => 'third_gear'
		),
		'shift_down' => array(
			'first_gear' => 'idling',
			'second_gear' => 'first_gear',
			'third_gear' => 'second_gear'
		),
		'crash' => array(
			'first_gear' => 'stalled',
			'second_gear' => 'stalled',
			'third_gear' => 'stalled'
		),
		'repair' => array(
			'stalled' => 'parked'
		),
		'idle' => array(
			'first_gear' => 'idling'
		),
		'turn_off' => array(
			'all' => 'parked'
		)
	);

    public function __construct($id = false, $ds = false, $table = false) {
        parent::__construct($id, $ds, $table);
        $this->on('ignite', 'after', function($prevState, $nextState, $transition) {
            // the car just ignited!
        });
    }

    // a shortcut method for checking if the vehicle is moving
    public function isMoving() {
        return in_array($this->getCurrentState(), array('first_gear', 'second_gear', 'third_gear'));
    }

    // the dependant function for "ignite"
	public function hasKey($role) {
		return $role == 'driver';
	}

}

使用上述模型,我们有以下方法

isParked()    onStateParked()
isStalled()   onStateStalled()
ignite()      canIgnite()         onBeforeIgnite()    onAfterIgnite()
park()        canPark()           onBeforePark()      onAfterPark()
isFirstGear() onStateFirstGear()
shiftUp()     canShiftUp()        onBeforeShiftUp()   onAfterShiftUp()

....
class Controller .... {
    public function method() {
        $this->Vehicle->create();
        $this->Vehicle->save(array(
            'Vehicle' => array(
                'title' => 'Toybota'
            )
        ));
        // $this->Vehicle->getCurrentState() == 'parked'
		if ($this->Vehicle->canIgnite('driver')) {
       	 	$this->Vehicle->ignite('driver');
       		$this->Vehicle->shiftUp();
        	// $this->Vehicle->getCurrentState() == 'first_gear'
		}
    }
}

Graphviz

如果您将以下状态机保存到 fsm.gv 并运行,则Vehicle的状态机将如下所示:

$model->toDot()

运行

dot -Tpng -ofsm.png fsm.gv