| Q | A |
| --- | --- |
| Bug fix? | no |
| New feature? | yes |
| BC breaks? | no |
| Deprecations? | no |
| Tests pass? | not yet |
| Fixed tickets | n/a |
| License | MIT |
| Doc PR | n/a |
TODO:
- [x] Add tests
- [x] Add PHP doc
- [x] Add Symfony fullstack integration (Config, DIC, command to dump the state-machine into graphiz format)
So why another component?
This component take another approach that what you can find on Packagist.
Here, the workflow component is not tied to a specific object like with Finite. It means that the component workflow is stateless and can be a symfony service.
Some code:
#!/usr/bin/env php
<?php
require __DIR__.'/vendor/autoload.php';
use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Component\Workflow\Definition;
use Symfony\Component\Workflow\Dumper\GraphvizDumper;
use Symfony\Component\Workflow\Marking;
use Symfony\Component\Workflow\MarkingStore\PropertyAccessorMarkingStore;
use Symfony\Component\Workflow\MarkingStore\ScalarMarkingStore;
use Symfony\Component\Workflow\Transition;
use Symfony\Component\Workflow\Workflow;
class Foo
{
public $marking;
public function __construct($init = 'a')
{
$this->marking = $init;
$this->marking = [$init => 1];
}
}
$fooDefinition = new Definition(Foo::class);
$fooDefinition->addPlaces([
'a', 'b', 'c', 'd', 'e', 'f', 'g',
]);
// name from to
$fooDefinition->addTransition(new Transition('t1', 'a', ['b', 'c']));
$fooDefinition->addTransition(new Transition('t2', ['b', 'c'], 'd'));
$fooDefinition->addTransition(new Transition('t3', 'd', 'e'));
$fooDefinition->addTransition(new Transition('t4', 'd', 'f'));
$fooDefinition->addTransition(new Transition('t5', 'e', 'g'));
$fooDefinition->addTransition(new Transition('t6', 'f', 'g'));
$graph = (new GraphvizDumper())->dump($fooDefinition);
$ed = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch(), new \Monolog\Logger('app'));
// $workflow = new Workflow($fooDefinition, new ScalarMarkingStore(), $ed);
$workflow = new Workflow($fooDefinition, new PropertyAccessorMarkingStore(), $ed);
$foo = new Foo(isset($argv[1]) ? $argv[1] : 'a');
$graph = (new GraphvizDumper())->dump($fooDefinition, $workflow->getMarking($foo));
dump([
'AvailableTransitions' => $workflow->getAvailableTransitions($foo),
'CurrentMarking' => clone $workflow->getMarking($foo),
'can validate t1' => $workflow->can($foo, 't1'),
'can validate t3' => $workflow->can($foo, 't3'),
'can validate t6' => $workflow->can($foo, 't6'),
'apply t1' => clone $workflow->apply($foo, 't1'),
'can validate t2' => $workflow->can($foo, 't2'),
'apply t2' => clone $workflow->apply($foo, 't2'),
'can validate t1 bis' => $workflow->can($foo, 't1'),
'can validate t3 bis' => $workflow->can($foo, 't3'),
'can validate t6 bis' => $workflow->can($foo, 't6'),
]);
The workflown:
The output:
array:10 [
"AvailableTransitions" => array:1 [
0 => Symfony\Component\Workflow\Transition {#4
-name: "t1"
-froms: array:1 [
0 => "a"
]
-tos: array:2 [
0 => "b"
1 => "c"
]
}
]
"CurrentMarking" => Symfony\Component\Workflow\Marking {#19
-places: array:1 [
"a" => true
]
}
"can validate t1" => true
"can validate t3" => false
"can validate t6" => false
"apply t1" => Symfony\Component\Workflow\Marking {#22
-places: array:2 [
"b" => true
"c" => true
]
}
"apply t2" => Symfony\Component\Workflow\Marking {#47
-places: array:1 [
"d" => true
]
}
"can validate t1 bis" => false
"can validate t3 bis" => true
"can validate t6 bis" => false
]
Feature