james.rus52 / state-machine

一个非常轻量且强大的PHP状态机

0.6 2020-12-26 18:35 UTC

This package is auto-updated.

Last update: 2024-09-27 02:51:50 UTC


README

定义你的状态,定义你的转换和回调:我们做剩下的。硬编码状态的年代已经过去了!

Build Status

安装(通过composer)

{
    "require": {
        "james.rus52/state-machine": "~0.5"
    }
}

使用

配置状态机图

为了使用状态机,您首先需要定义一个图。图是状态、转换以及可选的回调的定义;所有这些都与您的领域对象相关联。可以将多个图附加到同一对象上。

让我们为 DomainObject 对象定义一个名为 myGraphA 的图

$config = [
                  'graph'  => 'Request',
                  'property_path' => 'Status',
                  'states' => [
                      RequestStatus::NEW    => [
                          'actions' => [
                              RequestAction::COMMENT,
                              RequestAction::CLONE,
                              RequestAction::DELETE
                          ],
                          'properties' => [
                              RequestAction::COMMENT => ['b_name' => 's_comment' ],
                              RequestAction::CLONE => ['b_name' => 's_clone_request' ],
                              RequestAction::DELETE => ['b_name' => 's_delete_request' ,'roles' => ['author'], 'onclick' => 'ConfirmDeleteRequest();' ]
                          ]
                      ],
                      RequestStatus::ANALYZE    => [
                          'actions' => [
                              RequestAction::COMMENT,
                              RequestAction::CLONE,
                              RequestAction::ESCALATE,
                              RequestAction::DELETE,
                              RequestAction::SUSPEND,
                              RequestAction::UNSUSPEND,
                              ],
                          'properties' => [
                              RequestAction::COMMENT => ['b_name' => 's_comment' ],
                              RequestAction::CLONE => ['b_name' => 's_clone_request' ],
                              RequestAction::ESCALATE => ['b_name' => 's_escalation', 'onclick' => 'ShowModalWindow(\'owner_model_window\',\'SubmitForm\',\'owner_model_window_form\');' ],
                              RequestAction::DELETE => ['b_name' => 's_delete_request' ,'roles' => ['superadmin'], 'onclick' => 'ConfirmDeleteRequest();' ],
                              RequestAction::SUSPEND => ['b_name' => 's_suspend' ],
                              RequestAction::UNSUSPEND => ['b_name' => 's_unsuspend'],
                          ],
                          'conditions' => [
                              RequestAction::SUSPEND => ['object', 'isSuspended', false ],
                              RequestAction::UNSUSPEND => ['object', 'isSuspended', true ],
                          ]
                      ],
                      RequestStatus::APPROVE    => [
                          'actions' => [
                              RequestAction::COMMENT,
                              RequestAction::CLONE,
                              RequestAction::ESCALATE,
                              RequestAction::DELETE,
                              RequestAction::SUSPEND,
                              RequestAction::UNSUSPEND,
                              ],
                          'properties' => [
                              RequestAction::COMMENT => ['b_name' => 's_comment' ],
                              RequestAction::CLONE => ['b_name' => 's_clone_request' ],
                              RequestAction::ESCALATE => ['b_name' => 's_escalation', 'onclick' => 'ShowModalWindow(\'owner_model_window\',\'SubmitForm\',\'owner_model_window_form\');' ],
                              RequestAction::DELETE => ['b_name' => 's_delete_request' ,'roles' => ['superadmin'], 'onclick' => 'ConfirmDeleteRequest();' ],
                              RequestAction::SUSPEND => ['b_name' => 's_suspend' ],
                              RequestAction::UNSUSPEND => ['b_name' => 's_unsuspend'],
                          ],
                          'conditions' => [
                              RequestAction::SUSPEND => ['object', 'isSuspended', false ],
                              RequestAction::UNSUSPEND => ['object', 'isSuspended', true ],
                          ]
                      ],
                      RequestStatus::APPROVED    => [
                          'actions' => [
                              RequestAction::COMMENT,
                              RequestAction::CLONE,
                              RequestAction::ESCALATE,
                              RequestAction::DELETE,
                              RequestAction::SUSPEND,
                              RequestAction::UNSUSPEND,
                              ],
                          'properties' => [
                              RequestAction::COMMENT => ['b_name' => 's_comment' ],
                              RequestAction::CLONE => ['b_name' => 's_clone_request' ],
                              RequestAction::ESCALATE => ['b_name' => 's_escalation', 'onclick' => 'ShowModalWindow(\'owner_model_window\',\'SubmitForm\',\'owner_model_window_form\');' ],
                              RequestAction::DELETE => ['b_name' => 's_delete_request' ,'roles' => ['superadmin'], 'onclick' => 'ConfirmDeleteRequest();' ],
                              RequestAction::SUSPEND => ['b_name' => 's_suspend' ],
                              RequestAction::UNSUSPEND => ['b_name' => 's_unsuspend'],
                          ],
                          'conditions' => [
                              RequestAction::SUSPEND => ['object', 'isSuspended', false ],
                              RequestAction::UNSUSPEND => ['object', 'isSuspended', true ],
                          ]
                      ],
                      RequestStatus::SENT    => [
                          'actions' => [
                              RequestAction::COMMENT,
                              RequestAction::CLONE],
                          'properties' => [
                              RequestAction::COMMENT => ['b_name' => 's_comment' ],
                              RequestAction::CLONE => ['b_name' => 's_clone_request' ]
                          ]
                      ],
                      RequestStatus::DELIVERED    => [
                          'actions' => [
                              RequestAction::COMMENT,
                              RequestAction::CLONE,
                              RequestAction::ESCALATE,
                              RequestAction::SUSPEND,
                              RequestAction::UNSUSPEND,
                          ],
                          'properties' => [
                              RequestAction::COMMENT => ['b_name' => 's_comment' ],
                              RequestAction::CLONE => ['b_name' => 's_clone_request' ],
                              RequestAction::ESCALATE => ['b_name' => 's_escalation', 'onclick' => 'ShowModalWindow(\'owner_model_window\',\'SubmitForm\',\'owner_model_window_form\');' ],
                              RequestAction::SUSPEND => ['b_name' => 's_suspend' ],
                              RequestAction::UNSUSPEND => ['b_name' => 's_unsuspend'],
                          ],
                          'conditions' => [
                              RequestAction::SUSPEND => ['object', 'isSuspended', false ],
                              RequestAction::UNSUSPEND => ['object', 'isSuspended', true ],
                          ]
                      ],
                      RequestStatus::COMPLETED    => [
                          'actions' => [
                              RequestAction::COMMENT,
                              RequestAction::CLONE],
                          'properties' => [
                              RequestAction::COMMENT => ['b_name' => 's_comment' ],
                              RequestAction::CLONE => ['b_name' => 's_clone_request' ]
                          ]
                      ],
                      RequestStatus::CANCELED    => [
                          'actions' => [
                              RequestAction::COMMENT,
                              RequestAction::CLONE],
                          'properties' => [
                              RequestAction::COMMENT => ['b_name' => 's_comment' ],
                              RequestAction::CLONE => ['b_name' => 's_clone_request' ]
                          ]
                      ],
                      RequestStatus::REJECTED    => [
                          'actions' => [
                              RequestAction::COMMENT,
                              RequestAction::CLONE],
                          'properties' => [
                              RequestAction::COMMENT => ['b_name' => 's_comment' ],
                              RequestAction::CLONE => ['b_name' => 's_clone_request' ]
                          ]
                      ],
                  ],
                  'transitions' => [
                      RequestTransition::CANCEL  => [
                          'from' => [RequestStatus::NEW],
                          'to' => RequestStatus::CANCELED,
                          'properties' => ['b_name' => 's_to_cancel', 'css_class' => 'btn-outline-primary', 'roles' => ['executor','superadmin']]
                      ],
                      RequestTransition::TO_ANALYZE => [
                          'from' => [RequestStatus::NEW],
                          'to' => RequestStatus::ANALYZE,
                          'properties' => ['b_name' => 's_to_analyze', 'css_class' => 'btn-primary', 'roles' => ['executor','superadmin']]
                      ],
                      RequestTransition::REJECT  => [
                          'from' => [RequestStatus::ANALYZE],
                          'to' => RequestStatus::REJECTED,
                          'properties' => ['b_name' => 's_to_reject_admin', 'css_class' => 'btn-outline-primary', 'roles' => ['executor','superadmin']]
                      ],
                      RequestTransition::BACK_TO_AUTHOR  => [
                          'from' => [RequestStatus::ANALYZE],
                          'to' => RequestStatus::NEW,
                          'properties' => ['b_name' => 's_return_to_author', 'css_class' => 'btn-outline-primary', 'roles' => ['executor','superadmin']]
                      ],
                      RequestTransition::ANALYZE  => [
                          'from' => [RequestStatus::ANALYZE],
                          'to' => RequestStatus::APPROVE,
                          'properties' => ['b_name' => 's_aprove_my_resources', 'css_class' => 'btn-primary', 'roles' => ['executor','superadmin']]
                      ],
                      RequestTransition::RETURN  => [
                          'from' => [RequestStatus::APPROVE],
                          'to' => RequestStatus::ANALYZE,
                          'properties' => ['b_name' => 's_to_returnanalyze', 'css_class' => 'btn-outline-primary', 'roles' => ['executor','superadmin']]
                      ],
                      RequestTransition::REJECT  => [
                          'from' => [RequestStatus::APPROVE, RequestStatus::ANALYZE ],
                          'to' => RequestStatus::REJECTED,
                          'properties' => ['b_name' => 's_to_reject', 'css_class' => 'btn-outline-primary', 'roles' => ['executor','superadmin']]
                      ],
                      RequestTransition::APPROVE  => [
                          'from' => [RequestStatus::APPROVE],
                          'to' => RequestStatus::APPROVED,
                          'properties' => ['b_name' => 's_to_approve', 'css_class' => 'btn-primary', 'roles' => ['executor','superadmin']]
                      ],
                      RequestTransition::SEND  => [
                          'from' => [RequestStatus::APPROVED],
                          'to' => RequestStatus::SENT,
                          'properties' => ['b_name' => 's_to_partner', 'css_class' => 'btn-primary', 'roles' => ['executor','superadmin']]
                      ],
                      RequestTransition::DELIVER  => [
                          'from' => [RequestStatus::SENT],
                          'to' => RequestStatus::DELIVERED,
                          'properties' => ['roles' => ['system']]],
                      RequestTransition::RESEND => [
                          'from' => [RequestStatus::DELIVERED],
                          'to' => RequestStatus::SENT,
                          'properties' => ['b_name' => 's_resend', 'css_class' => 'btn-primary', 'roles' => ['executor','superadmin']]
                      ],
                      RequestTransition::COMPLETE  => [
                          'from' => [RequestStatus::DELIVERED],
                          'to' => RequestStatus::COMPLETED, 'properties' => ['roles' => ['system']]
                      ],
                      RequestTransition::REOPEN => [
                          'from' => [RequestStatus::COMPLETED, RequestStatus::REJECTED, RequestStatus::CANCELED],
                          'to' => RequestStatus::SENT,
                          'properties' => ['b_name' => 's_reopen', 'css_class' => 'btn-primary', 'roles' => ['author', 'executor','superadmin']]
                      ],
                  ],
                  'callbacks' => [
                      'lock' => [
                          [
                              'do'   => ['object','getLock'],
                          ],
                      ],
                      'unlock' => [
                          [
                              'do'   => ['object','releaseLock'],
                          ],
                      ],
                      'before' => [
                          [
                              'on' => RequestTransition::BACK_TO_AUTHOR,
                              'do'   => ['object','BackToAuthor'],
                              'args' => [$this->params]
                          ],
                          [
                              'on' => RequestTransition::CANCEL,
                              'do'   => ['object','Cancel'],
                              'args' => [$this->params]
                          ],
                          [
                              'on' => RequestTransition::TO_ANALYZE,
                              'do'   => ['object','ToAnalyze'],
                              'args' => [$this->params]
                          ],
                          [
                              'on' => RequestTransition::ANALYZE,
                              'do'   => ['object','ToApprove'],
                              'args' => [$this->params]
                          ],
                          [
                              'on' => RequestTransition::RETURN,
                              'do'   => ['object','BackToAnalyze'],
                              'args' => [$this->params]
                          ],
                          [
                              'on' => RequestTransition::REJECT,
                              'from' => RequestStatus::ANALYZE,
                              'do'   => ['object','ToRejectByAdmin'],
                              'args' => [$this->params]
                          ],
                          [
                              'on' => RequestTransition::REJECT,
                              'from' => RequestStatus::APPROVE,
                              'do'   => ['object','ToReject'],
                              'args' => [$this->params]
                          ],
                          [
                              'on' => RequestTransition::APPROVE,
                              'do'   => ['object','ToComplete'],
                              'args' => [$this->params]
                          ],
                          [
                              'on' => RequestTransition::SEND,
                              'do'   => ['object','ToSendPartner'],
                              'args' => [$this->params]
                          ],
                          [
                              'on' => RequestTransition::RESEND,
                              'do'   => ['object','ToSendPartner'],
                              'args' => [$this->params]
                          ],
                          [
                              'on' => RequestTransition::REOPEN,
                              'do'   => ['object','Reopen'],
                              'args' => [$this->params]
                          ],
                      ],
                      'action' => [
                          [
                              'action' => RequestAction::COMMENT,
                              'do'   => ['object','AddComment'],
                              'args' => [$this->params['ta_comment'] ?? null]
                          ],
                          [
                              'action' => RequestAction::CLONE,
                              'do'   => ['object','CloneRequest'],
                          ],
                          [
                              'action' => RequestAction::ESCALATE,
                              'on' => [RequestStatus::ANALYZE, RequestStatus::APPROVE, RequestStatus::APPROVED, RequestStatus::DELIVERED],
                              'do'   => ['object','Escalate'],
                              'args' => [$this->params['s_escalation_owner'] ?? null]
                          ],
                          [
                              'action' => RequestAction::DELETE,
                              'on' => [RequestStatus::NEW, RequestStatus::ANALYZE, RequestStatus::APPROVE, RequestStatus::APPROVED],
                              'do'   => ['object','DeleteRequest'],
                          ],
                          [
                              'action' => RequestAction::SUSPEND,
                              'do'   => ['object','Suspend'],
                              'args' => [$this->params['ta_comment'] ?? null]
                          ],
                          [
                              'action' => RequestAction::UNSUSPEND,
                              'do'   => ['object','Unsuspend'],
                              'args' => [$this->params['ta_comment'] ?? null]
                          ],
                      ],
                      'guard' => [
                          [
                              'on' => RequestTransition::RESEND,
                              'do' =>  ['object','hasResourcesError'],
                          ]
                      ]
                  ]
              ];

因此,在上面的例子中,图有6种可能的状态,通过对该对象应用一些转换可以实现这些状态。例如,在创建一个新的 DomainObject 时,您会应用 'create' 转换到对象上,之后其状态将变为 pending

使用状态机

定义

状态机是实际操作您的对象的那个对象。通过使用状态机,您可以测试转换是否可以应用,实际应用转换,检索当前状态等。状态机是特定于对象+图的。这意味着如果您想操作另一个对象,或者相同的对象使用另一个图,您需要另一个状态机

工厂帮助您获取这些对象+图的组合状态机。您向它提供一个对象和一个图名称,它将返回这对组合的状态机。如果您想在Symfony2应用程序中将此工厂作为服务,请参阅相应的StateMachineBundle

使用

请参阅 examples 文件夹中的几个示例。

回调

回调用于保护转换或在应用转换前后执行一些代码。

保护回调必须返回一个 bool。如果一个保护返回 false,则无法执行转换。

致谢

这个库受到了https://github.com/yohang/Finite的极大启发,但采取了不同的方向。

James的SM版本

https://github.com/sebdesign/state-machine克隆

我添加了一些新功能

  1. 属性 - 用户定义的信息,您可以通过
  • getTransitionProperties
  • getStateProperties
  • hasTransitionProperties
  • hasStateProperties
  1. 锁定/解锁 - 您可以实现锁定对象,当有人对您尝试转换的文档执行转换时
  2. 动作 - 状态可以有一些动作,这些动作不执行转换也不锁定文档。
  3. 条件 - 这是状态动作的条件,类似于转换的保护