alterway / component-workflow
此包最新版本(v1.0.0)没有可用的许可证信息。
Alter Way大量使用的工作流组件
v1.0.0
2014-02-18 13:47 UTC
Requires
- symfony/event-dispatcher: 2.3.*
Requires (Dev)
- atoum/atoum: dev-master
This package is not auto-updated.
Last update: 2024-09-24 07:22:08 UTC
README
此组件提供了一个PHP库编写的工作流引擎。
此库将工作流程视为一个简单的有向图:顶点代表节点,边代表转换。
节点
节点代表生命周期中的某个点。`Node` 类实现了这个概念。节点在整个工作流中通过唯一名称引用。约束是 `NodeMap` 类的责任。
转换
转换是节点之间的链接。《Transition》类实现了这个概念。在创建时,转换被赋予一个实现《SpecificationInterface》的规范对象。规范用作业务规则来决定在工作流中向何处推进。
令牌
令牌是用于初始化特定节点的工作流程的简单字符串。思想是将令牌视为放置在节点中心的物品。当工作流引擎启动时,令牌会从节点移动到节点。
事件
事件是每次令牌到达节点时创建的对象。《Event》类实现了这个概念。此类扩展了来自 Symfony EventDispatcher 组件的《Event》类。您可以为实现任何业务行为编写监听器或订阅者。
用法
假设您正在编写一个 PHP 博客引擎,并希望实现以下工作流程
- 一篇文章最初作为草稿存在
- 准备好后,文章会被发布
- 如果引起争议,文章会被删除
- 如果太旧,文章会被存档
首先,您需要为每个业务规则编写实现《SpecificationInterface》的类
namespace BlogEngine\Domain\Specification; use Alterway\Component\Workflow\ContextInterface; use Alterway\Component\Workflow\SpecificationInterface; class DraftableArticleSpecification implements SpecificationInterface { public function isSatisfiedBy(ContextInterface $context) { // an article can always be drafted return true; } } class PublishableArticleSpecification implements SpecificationInterface { public function isSatisfiedBy(ContextInterface $context) { // an article needs two reviews to be published return 1 < count($context->get('article')->getReviews()); } } class DeletableArticleSpecification implements SpecificationInterface { public function isSatisfiedBy(ContextInterface $context) { // an article can always be deleted if requested return 'delete' === $context->get('action'); } } class ArchivableArticleSpecification implements SpecificationInterface { public function isSatisfiedBy(ContextInterface $context) { // an article needs to be one month old to be archived $publishedAtPlusOneMonth = clone $context->get('publishedAt'); $publishedAtPlusOneMonth->modify('+1 month'); return 'archive' === $context->get('action') && $publishedAtPlusOneMonth < $context->get('now'); } }
然后,您可以使用《Builder》类和规范来描述工作流程
namespace BlogEngine\Domain\Service; use Alterway\Component\Workflow\Builder; use Alterway\Component\Workflow\ContextInterface; use BlogEngine\Domain\Event\ArticleSubscriber; use BlogEngine\Domain\Specification\DraftableArticleSpecification; use BlogEngine\Domain\Specification\PublishableArticleSpecification; use BlogEngine\Domain\Specification\DeletableArticleSpecification; use BlogEngine\Domain\Specification\ArchivableArticleSpecification; use BlogEngine\Util\Context; use Symfony\Component\EventDispatcher\EventDispatcherInterface; class ArticleService { private $workflow; public function __construct(EventDispatcherInterface $eventDispatcher) { $this->workflow = (new Builder($eventDispatcher)) ->open('article.draft', new DraftableArticleSpecification()) ->link('article.draft', 'article.published', new PublishableArticleSpecification()) ->link('article.published', 'article.deleted', new DeletableArticleSpecification()) ->link('article.published', 'article.archived', new ArchivableArticleSpecification()) ->getWorkflow(); $eventDispatcher->addSubscriber(new ArticleSubscriber()); } public function create(Article $article) { $this->advance($article, new Context()); } public function publish(Article $article) { $context = new Context(); $context->set('article', $article); $this->advance($article, $context); } public function delete(Article $article) { $context = new Context(); $context->set('action', 'delete'); $this->advance($article, $context); } public function archive(Article $article) { $context = new Context(); $context->set('action', 'archive'); $context->set('publishedAt', $article->getPublishedAt()); $context->set('now', new \DateTime()); $this->advance($article, $context); } private function advance($article, ContextInterface $context) { try { $this->workflow->initialize($article->getToken())->next($context); } catch (\LogicException $e) { // the workflow reports a problem } } }
最后,您必须监听工作流程派发的事件来附加业务行为
namespace BlogEngine\Domain\Event; use Alterway\Component\Workflow\Event; use Symfony\Component\EventDispatcher\EventSubscriberInterface; class ArticleSubscriber implements EventSubscriberInterface { public static function getSubscribedEvents() { return array( 'article.draft' => array('onDraft', 0), 'article.published' => array('onPublished', 0), 'article.deleted' => array('onDeleted', 0), 'article.archived' => array('onArchived', 0), ); } public function onDraft(Event $event) { /* ... */ } public function onPublished(Event $event) { /* ... */ } public function onDeleted(Event $event) { /* ... */ } public function onArchived(Event $event) { /* ... */ } }
贡献
非常感谢,加上糖,提供了 phpspec 规范,并在贡献代码时应为绿色。
参考
理论
PHP
许可
有关详细信息,请参阅捆绑的 LICENSE 文件。