dev-master
9999999-devWorkflow component heavily used by Alter Way
MIT
The Requires
The Development Requires
v1.0.0
1.0.0.0Workflow component heavily used by Alter Way
The Requires
The Development Requires
Wallogit.com
2017 © Pedro Peláez
Workflow component heavily used by Alter Way
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)
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)
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)
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)
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)
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) { /* ... */ }
}
Pretty please, with sugar on top, phpspec specifications are provided and should be green when contributing code., (*11)
See the bundled LICENSE file for details., (*12)
Workflow component heavily used by Alter Way
MIT
Workflow component heavily used by Alter Way