added the first more-or-less working version of the Workflow component
This commit is contained in:
parent
9af416d096
commit
17d59a7c66
87
src/Symfony/Component/Workflow/Definition.php
Normal file
87
src/Symfony/Component/Workflow/Definition.php
Normal file
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Workflow;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Definition
|
||||
{
|
||||
private $class;
|
||||
private $states = array();
|
||||
private $transitions = array();
|
||||
private $initialState;
|
||||
|
||||
public function __construct($class)
|
||||
{
|
||||
$this->class = $class;
|
||||
}
|
||||
|
||||
public function getClass()
|
||||
{
|
||||
return $this->class;
|
||||
}
|
||||
|
||||
public function getStates()
|
||||
{
|
||||
return $this->states;
|
||||
}
|
||||
|
||||
public function getTransitions()
|
||||
{
|
||||
return $this->transitions;
|
||||
}
|
||||
|
||||
public function getInitialState()
|
||||
{
|
||||
return $this->initialState;
|
||||
}
|
||||
|
||||
public function setInitialState($name)
|
||||
{
|
||||
if (!isset($this->states[$name])) {
|
||||
throw new \LogicException(sprintf('State "%s" cannot be the initial state as it does not exist.', $name));
|
||||
}
|
||||
|
||||
$this->initialState = $name;
|
||||
}
|
||||
|
||||
public function addState($name)
|
||||
{
|
||||
if (!count($this->states)) {
|
||||
$this->initialState = $name;
|
||||
}
|
||||
|
||||
$this->states[$name] = $name;
|
||||
}
|
||||
|
||||
public function addTransition(Transition $transition)
|
||||
{
|
||||
if (isset($this->transitions[$transition->getName()])) {
|
||||
throw new \LogicException(sprintf('Transition "%s" is already defined.', $transition->getName()));
|
||||
}
|
||||
|
||||
foreach ($transition->getFroms() as $from) {
|
||||
if (!isset($this->states[$from])) {
|
||||
throw new \LogicException(sprintf('State "%s" referenced in transition "%s" does not exist.', $from, $name));
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($transition->getTos() as $to) {
|
||||
if (!isset($this->states[$to])) {
|
||||
throw new \LogicException(sprintf('State "%s" referenced in transition "%s" does not exist.', $to, $name));
|
||||
}
|
||||
}
|
||||
|
||||
$this->transitions[$transition->getName()] = $transition;
|
||||
}
|
||||
}
|
32
src/Symfony/Component/Workflow/Dumper/DumperInterface.php
Normal file
32
src/Symfony/Component/Workflow/Dumper/DumperInterface.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Workflow\Dumper;
|
||||
|
||||
use Symfony\Component\Workflow\Definition;
|
||||
|
||||
/**
|
||||
* DumperInterface is the interface implemented by workflow dumper classes.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface DumperInterface
|
||||
{
|
||||
/**
|
||||
* Dumps a workflow definition.
|
||||
*
|
||||
* @param Definition $definition A Definition instance
|
||||
* @param array $options An array of options
|
||||
*
|
||||
* @return string The representation of the workflow
|
||||
*/
|
||||
public function dump(Definition $definition, array $options = array());
|
||||
}
|
198
src/Symfony/Component/Workflow/Dumper/GraphvizDumper.php
Normal file
198
src/Symfony/Component/Workflow/Dumper/GraphvizDumper.php
Normal file
@ -0,0 +1,198 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Workflow\Dumper;
|
||||
|
||||
use Symfony\Component\Workflow\Definition;
|
||||
|
||||
/**
|
||||
* GraphvizDumper dumps a workflow as a graphviz file.
|
||||
*
|
||||
* You can convert the generated dot file with the dot utility (http://www.graphviz.org/):
|
||||
*
|
||||
* dot -Tpng workflow.dot > workflow.png
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class GraphvizDumper implements DumperInterface
|
||||
{
|
||||
private $nodes;
|
||||
private $edges;
|
||||
private $options = array(
|
||||
'graph' => array('ratio' => 'compress', 'rankdir' => 'LR'),
|
||||
'node' => array('fontsize' => 9, 'fontname' => 'Arial', 'color' => '#333', 'shape' => 'circle', 'fillcolor' => 'lightblue', 'fixedsize' => true, 'width' => 1),
|
||||
'edge' => array('fontsize' => 9, 'fontname' => 'Arial', 'color' => '#333', 'arrowhead' => 'normal', 'arrowsize' => 0.5),
|
||||
);
|
||||
|
||||
/**
|
||||
* Dumps the workflow as a graphviz graph.
|
||||
*
|
||||
* Available options:
|
||||
*
|
||||
* * graph: The default options for the whole graph
|
||||
* * node: The default options for nodes
|
||||
* * edge: The default options for edges
|
||||
*
|
||||
* @param Definition $definition A Definition instance
|
||||
* @param array $options An array of options
|
||||
*
|
||||
* @return string The dot representation of the workflow
|
||||
*/
|
||||
public function dump(Definition $definition, array $options = array())
|
||||
{
|
||||
foreach (array('graph', 'node', 'edge') as $key) {
|
||||
if (isset($options[$key])) {
|
||||
$this->options[$key] = array_merge($this->options[$key], $options[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->nodes = $this->findNodes($definition);
|
||||
$this->edges = $this->findEdges($definition);
|
||||
|
||||
return $this->startDot().$this->addNodes().$this->addEdges().$this->endDot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all nodes.
|
||||
*
|
||||
* @return array An array of all nodes
|
||||
*/
|
||||
private function findNodes(Definition $definition)
|
||||
{
|
||||
$nodes = array();
|
||||
foreach ($definition->getStates() as $state) {
|
||||
$nodes[$state] = array(
|
||||
'attributes' => array_merge($this->options['node'], array('style' => $state == $definition->getInitialState() ? 'filled' : 'solid'))
|
||||
);
|
||||
}
|
||||
|
||||
return $nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all nodes.
|
||||
*
|
||||
* @return string A string representation of all nodes
|
||||
*/
|
||||
private function addNodes()
|
||||
{
|
||||
$code = '';
|
||||
foreach ($this->nodes as $id => $node) {
|
||||
$code .= sprintf(" node_%s [label=\"%s\", shape=%s%s];\n", $this->dotize($id), $id, $this->options['node']['shape'], $this->addAttributes($node['attributes']));
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
private function findEdges(Definition $definition)
|
||||
{
|
||||
$edges = array();
|
||||
foreach ($definition->getTransitions() as $transition) {
|
||||
foreach ($transition->getFroms() as $from) {
|
||||
foreach ($transition->getTos() as $to) {
|
||||
$edges[$from][] = array(
|
||||
'name' => $transition->getName(),
|
||||
'to' => $to,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $edges;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all edges.
|
||||
*
|
||||
* @return string A string representation of all edges
|
||||
*/
|
||||
private function addEdges()
|
||||
{
|
||||
$code = '';
|
||||
foreach ($this->edges as $id => $edges) {
|
||||
foreach ($edges as $edge) {
|
||||
$code .= sprintf(" node_%s -> node_%s [label=\"%s\" style=\"%s\"];\n", $this->dotize($id), $this->dotize($edge['to']), $edge['name'], 'solid');
|
||||
}
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the start dot.
|
||||
*
|
||||
* @return string The string representation of a start dot
|
||||
*/
|
||||
private function startDot()
|
||||
{
|
||||
return sprintf("digraph workflow {\n %s\n node [%s];\n edge [%s];\n\n",
|
||||
$this->addOptions($this->options['graph']),
|
||||
$this->addOptions($this->options['node']),
|
||||
$this->addOptions($this->options['edge'])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the end dot.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function endDot()
|
||||
{
|
||||
return "}\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds attributes
|
||||
*
|
||||
* @param array $attributes An array of attributes
|
||||
*
|
||||
* @return string A comma separated list of attributes
|
||||
*/
|
||||
private function addAttributes($attributes)
|
||||
{
|
||||
$code = array();
|
||||
foreach ($attributes as $k => $v) {
|
||||
$code[] = sprintf('%s="%s"', $k, $v);
|
||||
}
|
||||
|
||||
return $code ? ', '.implode(', ', $code) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds options
|
||||
*
|
||||
* @param array $options An array of options
|
||||
*
|
||||
* @return string A space separated list of options
|
||||
*/
|
||||
private function addOptions($options)
|
||||
{
|
||||
$code = array();
|
||||
foreach ($options as $k => $v) {
|
||||
$code[] = sprintf('%s="%s"', $k, $v);
|
||||
}
|
||||
|
||||
return implode(' ', $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dotizes an identifier.
|
||||
*
|
||||
* @param string $id The identifier to dotize
|
||||
*
|
||||
* @return string A dotized string
|
||||
*/
|
||||
private function dotize($id)
|
||||
{
|
||||
return strtolower(preg_replace('/[^\w]/i', '_', $id));
|
||||
}
|
||||
}
|
51
src/Symfony/Component/Workflow/Event/Event.php
Normal file
51
src/Symfony/Component/Workflow/Event/Event.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Workflow\Event;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event as BaseEvent;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Event extends BaseEvent
|
||||
{
|
||||
private $object;
|
||||
private $state;
|
||||
private $attributes;
|
||||
|
||||
public function __construct($object, $state, array $attributes = array())
|
||||
{
|
||||
$this->object = $object;
|
||||
$this->state = $state;
|
||||
$this->attributes = $attributes;
|
||||
}
|
||||
|
||||
public function getState()
|
||||
{
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
public function getObject()
|
||||
{
|
||||
return $this->object;
|
||||
}
|
||||
|
||||
public function getAttribute($key)
|
||||
{
|
||||
return isset($this->attributes[$key]) ? $this->attributes[$key] : null;
|
||||
}
|
||||
|
||||
public function hastAttribute($key)
|
||||
{
|
||||
return isset($this->attributes[$key]);
|
||||
}
|
||||
}
|
30
src/Symfony/Component/Workflow/Event/GuardEvent.php
Normal file
30
src/Symfony/Component/Workflow/Event/GuardEvent.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Workflow\Event;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class GuardEvent extends Event
|
||||
{
|
||||
private $allowed = null;
|
||||
|
||||
public function isAllowed()
|
||||
{
|
||||
return $this->allowed;
|
||||
}
|
||||
|
||||
public function setAllowed($allowed)
|
||||
{
|
||||
$this->allowed = (Boolean) $allowed;
|
||||
}
|
||||
}
|
30
src/Symfony/Component/Workflow/Event/TransitionEvent.php
Normal file
30
src/Symfony/Component/Workflow/Event/TransitionEvent.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Workflow\Event;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class TransitionEvent extends Event
|
||||
{
|
||||
private $nextState;
|
||||
|
||||
public function setNextState($state)
|
||||
{
|
||||
$this->nextState = $state;
|
||||
}
|
||||
|
||||
public function getNextState()
|
||||
{
|
||||
return $this->nextState;
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Workflow\EventListener;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Workflow\Event\Event;
|
||||
|
||||
class AuditTrailListener implements EventSubscriberInterface
|
||||
{
|
||||
public function onEnter(Event $event)
|
||||
{
|
||||
// FIXME: object "identity", timestamp, who, ...
|
||||
error_log('entering "'.$event->getState().'" generic for object of class '.get_class($event->getObject()));
|
||||
}
|
||||
|
||||
public function onLeave(Event $event)
|
||||
{
|
||||
error_log('leaving "'.$event->getState().'" generic for object of class '.get_class($event->getObject()));
|
||||
}
|
||||
|
||||
public function onTransition(Event $event)
|
||||
{
|
||||
error_log('transition "'.$event->getState().'" generic for object of class '.get_class($event->getObject()));
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
// FIXME: add a way to listen to workflow.XXX.*
|
||||
'workflow.transition' => array('onTransition'),
|
||||
'workflow.leave' => array('onLeave'),
|
||||
'workflow.enter' => array('onEnter'),
|
||||
);
|
||||
}
|
||||
}
|
43
src/Symfony/Component/Workflow/Registry.php
Normal file
43
src/Symfony/Component/Workflow/Registry.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Workflow;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Registry
|
||||
{
|
||||
private $workflows = array();
|
||||
|
||||
public function __construct(array $workflows = array())
|
||||
{
|
||||
foreach ($workflows as $workflow) {
|
||||
$this->add($workflow);
|
||||
}
|
||||
}
|
||||
|
||||
public function add(Workflow $workflow)
|
||||
{
|
||||
$this->workflows[] = $workflow;
|
||||
}
|
||||
|
||||
public function get($object)
|
||||
{
|
||||
foreach ($this->workflows as $workflow) {
|
||||
if ($workflow->supports($object)) {
|
||||
return $workflow;
|
||||
}
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException(sprintf('Unable to find a workflow for class "%s".', get_class($object)));
|
||||
}
|
||||
}
|
44
src/Symfony/Component/Workflow/Transition.php
Normal file
44
src/Symfony/Component/Workflow/Transition.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Workflow;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Transition
|
||||
{
|
||||
private $name;
|
||||
private $froms = array();
|
||||
private $tos = array();
|
||||
|
||||
public function __construct($name, $froms, $tos)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->froms = (array) $froms;
|
||||
$this->tos = (array) $tos;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getFroms()
|
||||
{
|
||||
return $this->froms;
|
||||
}
|
||||
|
||||
public function getTos()
|
||||
{
|
||||
return $this->tos;
|
||||
}
|
||||
}
|
208
src/Symfony/Component/Workflow/Workflow.php
Normal file
208
src/Symfony/Component/Workflow/Workflow.php
Normal file
@ -0,0 +1,208 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Workflow;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Workflow\Event\Event;
|
||||
use Symfony\Component\Workflow\Event\GuardEvent;
|
||||
use Symfony\Component\Workflow\Event\TransitionEvent;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessor;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Workflow
|
||||
{
|
||||
private $name;
|
||||
private $dispatcher;
|
||||
private $propertyAccessor;
|
||||
private $property = 'state';
|
||||
private $stateTransitions = array();
|
||||
private $states;
|
||||
private $initialState;
|
||||
private $class;
|
||||
|
||||
public function __construct($name, Definition $definition, EventDispatcherInterface $dispatcher = null)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->dispatcher = $dispatcher;
|
||||
$this->propertyAccessor = PropertyAccess::createPropertyAccessor();
|
||||
|
||||
$this->states = $definition->getStates();
|
||||
$this->class = $definition->getClass();
|
||||
$this->initialState = $definition->getInitialState();
|
||||
foreach ($definition->getTransitions() as $name => $transition) {
|
||||
$this->transitions[$name] = $transition;
|
||||
foreach ($transition->getFroms() as $from) {
|
||||
$this->stateTransitions[$from][$name] = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function supports($class)
|
||||
{
|
||||
return $class instanceof $this->class;
|
||||
}
|
||||
|
||||
public function can($object, $transition)
|
||||
{
|
||||
if (!isset($this->transitions[$transition])) {
|
||||
throw new \LogicException(sprintf('Transition "%s" does not exist for workflow "%s".', $transition, $this->name));
|
||||
}
|
||||
|
||||
if (null !== $this->dispatcher) {
|
||||
$event = new GuardEvent($object, $this->getState($object));
|
||||
|
||||
$this->dispatcher->dispatch(sprintf('workflow.%s.guard.%s', $this->name, $transition), $event);
|
||||
|
||||
if (null !== $ret = $event->isAllowed()) {
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
||||
return isset($this->stateTransitions[$this->getState($object)][$transition]);
|
||||
}
|
||||
|
||||
public function getState($object)
|
||||
{
|
||||
$state = $this->propertyAccessor->getValue($object, $this->property);
|
||||
|
||||
// check if the object is already in the workflow
|
||||
if (null === $state) {
|
||||
$this->enter($object, $this->initialState, array());
|
||||
|
||||
$state = $this->propertyAccessor->getValue($object, $this->property);
|
||||
}
|
||||
|
||||
// check that the object has a known state
|
||||
if (!isset($this->states[$state])) {
|
||||
throw new \LogicException(sprintf('State "%s" is not valid for workflow "%s".', $transition, $this->name));
|
||||
}
|
||||
|
||||
return $state;
|
||||
}
|
||||
|
||||
public function apply($object, $transition, array $attributes = array())
|
||||
{
|
||||
$current = $this->getState($object);
|
||||
|
||||
if (!$this->can($object, $transition)) {
|
||||
throw new \LogicException(sprintf('Unable to apply transition "%s" from state "%s" for workflow "%s".', $transition, $current, $this->name));
|
||||
}
|
||||
|
||||
$transition = $this->determineTransition($current, $transition);
|
||||
|
||||
$this->leave($object, $current, $attributes);
|
||||
|
||||
$state = $this->transition($object, $current, $transition, $attributes);
|
||||
|
||||
$this->enter($object, $state, $attributes);
|
||||
}
|
||||
|
||||
public function getAvailableTransitions($object)
|
||||
{
|
||||
return array_keys($this->stateTransitions[$this->getState($object)]);
|
||||
}
|
||||
|
||||
public function getNextStates($object)
|
||||
{
|
||||
if (!$stateTransitions = $this->stateTransitions[$this->getState($object)]) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$states = array();
|
||||
foreach ($stateTransitions as $transition) {
|
||||
foreach ($this->transitions[$transition]->getTos() as $to) {
|
||||
$states[] = $to;
|
||||
}
|
||||
}
|
||||
|
||||
return $states;
|
||||
}
|
||||
|
||||
public function setStateProperty($property)
|
||||
{
|
||||
$this->property = $property;
|
||||
}
|
||||
|
||||
public function setPropertyAccessor(PropertyAccessor $propertyAccessor)
|
||||
{
|
||||
$this->propertyAccessor = $propertyAccessor;
|
||||
}
|
||||
|
||||
public function __call($method, $arguments)
|
||||
{
|
||||
if (!count($arguments)) {
|
||||
throw new BadMethodCallException();
|
||||
}
|
||||
|
||||
return $this->apply($arguments[0], $method, array_slice($arguments, 1));
|
||||
}
|
||||
|
||||
private function leave($object, $state, $attributes)
|
||||
{
|
||||
if (null === $this->dispatcher) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->dispatcher->dispatch(sprintf('workflow.leave', $this->name), new Event($object, $state, $attributes));
|
||||
$this->dispatcher->dispatch(sprintf('workflow.%s.leave', $this->name), new Event($object, $state, $attributes));
|
||||
$this->dispatcher->dispatch(sprintf('workflow.%s.leave.%s', $this->name, $state), new Event($object, $state, $attributes));
|
||||
}
|
||||
|
||||
private function transition($object, $current, Transition $transition, $attributes)
|
||||
{
|
||||
$state = null;
|
||||
$tos = $transition->getTos();
|
||||
|
||||
if (null !== $this->dispatcher) {
|
||||
// the generic event cannot change the next state
|
||||
$this->dispatcher->dispatch(sprintf('workflow.transition', $this->name), new Event($object, $current, $attributes));
|
||||
$this->dispatcher->dispatch(sprintf('workflow.%s.transition', $this->name), new Event($object, $current, $attributes));
|
||||
|
||||
$event = new TransitionEvent($object, $current, $attributes);
|
||||
$this->dispatcher->dispatch(sprintf('workflow.%s.transition.%s', $this->name, $transition->getName()), $event);
|
||||
$state = $event->getNextState();
|
||||
|
||||
if (null !== $state && !in_array($state, $tos)) {
|
||||
throw new \LogicException(sprintf('Transition "%s" cannot go to state "%s" for workflow "%s"', $transition->getName(), $state, $this->name));
|
||||
}
|
||||
}
|
||||
|
||||
if (null === $state) {
|
||||
if (count($tos) > 1) {
|
||||
throw new \LogicException(sprintf('Unable to apply transition "%s" as the new state is not unique for workflow "%s".', $transition->getName(), $this->name));
|
||||
}
|
||||
|
||||
$state = $tos[0];
|
||||
}
|
||||
|
||||
return $state;
|
||||
}
|
||||
|
||||
private function enter($object, $state, $attributes)
|
||||
{
|
||||
$this->propertyAccessor->setValue($object, $this->property, $state);
|
||||
|
||||
if (null !== $this->dispatcher) {
|
||||
$this->dispatcher->dispatch(sprintf('workflow.enter', $this->name), new Event($object, $state, $attributes));
|
||||
$this->dispatcher->dispatch(sprintf('workflow.%s.enter', $this->name), new Event($object, $state, $attributes));
|
||||
$this->dispatcher->dispatch(sprintf('workflow.%s.enter.%s', $this->name, $state), new Event($object, $state, $attributes));
|
||||
}
|
||||
}
|
||||
|
||||
private function determineTransition($current, $transition)
|
||||
{
|
||||
return $this->transitions[$transition];
|
||||
}
|
||||
}
|
36
src/Symfony/Component/Workflow/composer.json
Normal file
36
src/Symfony/Component/Workflow/composer.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "symfony/workflow",
|
||||
"type": "library",
|
||||
"description": "Symfony Workflow Component",
|
||||
"keywords": [],
|
||||
"homepage": "http://symfony.com",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "http://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.3",
|
||||
"symfony/event-dispatcher": "~2.1",
|
||||
"symfony/property-access": "~2.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"twig/twig": "~1.14"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": { "Symfony\\Component\\Workflow\\": "" }
|
||||
},
|
||||
"target-dir": "Symfony/Component/Workflow",
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.5-dev"
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user