定制 alterway/component-workflow 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

alterway/component-workflow

Composer 安装命令:

composer require alterway/component-workflow

包简介

Workflow component heavily used by Alter Way

README 文档

README

This component provides a workflow engine written as a PHP library.

Instead of modeling a workflow as a Petri net or trying to enumerate workflow patterns, the library consider a workflow as a simple directed graph: vertices model nodes and edges model transitions.

Nodes

A node represents a point in a life cycle. The Node class implements the concept. A node is referenced by a unique name across the workflow. The constraint is the responsibility of NodeMap class.

Transitions

A transition is a link between nodes. The Transition class implements the concept. At creation, a transition is given a specification object implementing the SpecificationInterface. the specification is used as a business rule to decide where to advance in the workflow.

Tokens

A token is a simple string used to initialize the workflow in a particular node. The idea is to consider the token as a thing placed at the center of a node. When workflow engine is on, the token is moving from node to node.

Events

An event is an object created each time a token arrives at a node. The Event class implements the concept. This class extends the Event class from the Symfony EventDispatcher component. You can write listeners or subscribers to implement any business behaviour.

Usage

Let's say you are writing a blog engine in PHP and you want to implement the following workflow:

  • an article begins its existence as a draft
  • when ready, the article gets published
  • if controversial, the article is deleted
  • when too old, the article is archived

First of all, you need to write classes implementing SpecificationInterface for every business rule:

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');
    }
}

Then, you can use the Builder class and the specifications to describe the workflow:

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
        }
    }
}

Finally, you have to listen on events dispatched by the workflow to attach the business behavior:

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) { /* ... */ }
}

Contributing

Pretty please, with sugar on top, phpspec specifications are provided and should be green when contributing code.

References

Theory

PHP

Licencing

See the bundled LICENSE file for details.

Sponsors

统计信息

  • 总下载量: 3.66k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 65
  • 点击次数: 1
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 65
  • Watchers: 14
  • Forks: 9
  • 开发语言: PHP

其他信息

  • 授权协议: Unknown
  • 更新时间: 2014-02-18

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固