2017 © Pedro Peláez
 

library component-workflow

Workflow component heavily used by Alter Way

image

alterway/component-workflow

Workflow component heavily used by Alter Way

  • Monday, December 29, 2014
  • by herveleclerc
  • Repository
  • 15 Watchers
  • 71 Stars
  • 3,606 Installations
  • PHP
  • 0 Dependents
  • 0 Suggesters
  • 11 Forks
  • 1 Open issues
  • 2 Versions
  • 0 % Grown

The README.md

Workflow Component Build Status

This component provides a workflow engine written as a PHP library., (*1)

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., (*2)

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., (*3)

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., (*4)

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., (*5)

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., (*6)

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, (*7)

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

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:, (*9)

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:, (*10)

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., (*11)

References

Theory

PHP

Licencing

See the bundled LICENSE file for details., (*12)

Sponsors

The Versions

29/12 2014

dev-master

9999999-dev

Workflow component heavily used by Alter Way

  Sources   Download

MIT

The Requires

 

The Development Requires

18/02 2014

v1.0.0

1.0.0.0

Workflow component heavily used by Alter Way

  Sources   Download

The Requires

 

The Development Requires