star / state-machine
PHP 实现的可配置状态机、工作流和领域模型的转换
Requires
- php: ^7.4|^8.0
- symfony/event-dispatcher: ^4.0|^5.0|^6.0
- webmozart/assert: ^1.0
Requires (Dev)
- ext-pdo: *
- doctrine/annotations: ^1.13
- doctrine/orm: ^2.5
- infection/infection: ~0.13
- phpstan/phpstan: ^1.0
- phpstan/phpstan-phpunit: ^1.0
- phpunit/phpunit: ^8.5
- squizlabs/php_codesniffer: ^3.2
- symfony/cache: ^4.0|^5.0|^6.0
This package is auto-updated.
Last update: 2024-09-16 21:26:16 UTC
README
此包可以帮助您为特定上下文构建工作流,该工作流可以封装在给定的上下文中。
它被设计为避免对包有硬依赖。该库不需要您实现任何方法。您需要编写的所有代码都可以封装在您的上下文类中,并且对其他对象保持隐藏。
安装
composer require star/state-machine
特性
状态
状态只是上下文可能处于的名称。状态通常保存在持久化平台中,或者使用一个 字符串 表示或一个 StateMetadata 类来保存在模型中。
转换
转换是对上下文的一个操作,可以将上下文从一个状态移动到另一个状态。转换只能有一个目标状态,因为没有方法让机器知道要移动到哪个状态。另一方面,转换可能有多个起始状态。
如果没有转换包含上下文的当前状态作为起始状态,将引发异常(除非提供了另一个 TransitionCallback)。
属性
属性用于标记状态对上下文有含义。
例如,如果您需要将状态视为活动状态或已关闭状态,而另一个状态不应如此,您只需将 is_active
和 is_closed
属性添加到需要它们的那些状态中即可。
// your context using the builder public function isActive() { return $this->stateMachine()-hasAttribute("is_active"); }
// your context using the metadata public function isActive() { return $this->state->hasAttribute("is_active"); }
使用示例
假设您有一个 Post
上下文,它可以有以下状态
- 草案:该文章仅对创建者和版主可见
- 已发布:该文章对所有用户可见
- 存档:该文章仅对创建者可见
该文章允许的工作流如下
您可以将 Post
类定义为以下模式之一。
在模型中使用构建器
class Post { /** * @var string */ private $state; public function publish() { $this->state = $this->stateMachine()->transit("publish", $this); } public function archive() { $this->state = $this->stateMachine()->transit("archive", $this); } public function unarchive() { $this->state = $this->stateMachine()->transit("unarchive", $this); } public function isClosed() { return $this->stateMachine()->hasAttribute("is_closed"); } /** * @return StateMachine */ private function stateMachine() { return StateBuilder::build() ->allowTransition("publish", "draft", "published") ->allowTransition("archive", "published", "archived") ->allowTransition("unarchive", "published", "draft") ->addAttribute("is_closed", ["archived", "drafted"]) ->create($this->state); } }
将工作流封装在类中
如果您有多个模型可以具有相同的工作流,可以使用 StateMetadata 定义一个封装工作流的类。
final class MyStateWorkflow extends StateMetadata { protected function __construct() { parent::__construct('pending'); } protected function createMachine(StateBuilder $builder) { $builder->allowTransition("publish", "draft", "published") $builder->allowTransition("archive", "published", "archived") $builder->allowTransition("unarchive", "published", "draft") $builder->addAttribute("is_closed", ["archived", "drafted"]) } } class Post { /** * @var string */ private $state; public function __construct() { $this->>state = new MyStateWorkflow(); } public function publish() { $this->state = $this->state->transit("publish", $this); } public function archive() { $this->state = $this->state->transit("archive", $this); } public function unarchive() { $this->state = $this->state->transit("unarchive", $this); } public function isClosed() { return $this->state->hasAttribute("is_closed"); } }
状态的持久化
该包支持以下持久化引擎
事件
状态机有一个内部事件处理系统。
在多个位置触发多个事件,这使得您可以在某些转换上挂钩系统以添加行为。
监听这些事件的订阅者将对其配置的回调进行调用。
StateEventStore::BEFORE_TRANSITION
:在上下文的任何转换之前执行事件。请参阅TransitionWasRequested
。StateEventStore::AFTER_TRANSITION
:该事件在任何转换在上下文中执行后执行。参见TransitionWasSuccessful
。StateEventStore::FAILURE_TRANSITION
:该事件在转换异常触发之前执行。参见TransitionWasFailed
。
在机器中订阅监听器
$stateMachine->addListener( StateEventStore::BEFORE_TRANSITION, function(TransitionWasRequested $event) { // do something } );
转换回调
在请求转换时,另一种将钩子连接到过程的方式是传递一个 转换回调。
转换回调允许在转换之前、之后或转换不被允许时执行一个动作。默认情况下,将触发一个异常。参见 AlwaysThrowExceptionOnFailure。
转换上的回调
$this->state->transit("transition", $this, new DoSomethingOnSuccessIfConditionMatches());