Skip to content

Commit 2a6af4e

Browse files
Nyholmlyrixx
authored andcommitted
Make the Workflow support State Machines
1 parent d8777a7 commit 2a6af4e

File tree

5 files changed

+110
-3
lines changed

5 files changed

+110
-3
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler;
13+
14+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
15+
use Symfony\Component\DependencyInjection\ContainerBuilder;
16+
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
17+
use Symfony\Component\Workflow\Validator\DefinitionValidatorInterface;
18+
use Symfony\Component\Workflow\Validator\SinglePlaceWorkflowValidator;
19+
use Symfony\Component\Workflow\Validator\StateMachineValidator;
20+
use Symfony\Component\Workflow\Validator\WorkflowValidator;
21+
22+
/**
23+
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
24+
*/
25+
class ValidateWorkflowsPass implements CompilerPassInterface
26+
{
27+
/**
28+
* @var DefinitionValidatorInterface[]
29+
*/
30+
private $validators = array();
31+
32+
public function process(ContainerBuilder $container)
33+
{
34+
$taggedServices = $container->findTaggedServiceIds('workflow.definition');
35+
foreach ($taggedServices as $id => $tags) {
36+
$definition = $container->get($id);
37+
foreach ($tags as $tag) {
38+
if (empty($tag['name'])) {
39+
throw new RuntimeException(sprintf('The "name" for the tag "workflow.definition" of service "%s" must be set.', $id));
40+
}
41+
if (empty($tag['type'])) {
42+
throw new RuntimeException(sprintf('The "type" for the tag "workflow.definition" of service "%s" must be set.', $id));
43+
}
44+
if (empty($tag['marking_store'])) {
45+
throw new RuntimeException(sprintf('The "marking_store" for the tag "workflow.definition" of service "%s" must be set.', $id));
46+
}
47+
48+
$this->getValidator($tag)->validate($definition, $tag['name']);
49+
}
50+
}
51+
}
52+
53+
/**
54+
* @param array $tag
55+
*
56+
* @return DefinitionValidatorInterface
57+
*/
58+
private function getValidator($tag)
59+
{
60+
if ($tag['type'] === 'state_machine') {
61+
$name = 'state_machine';
62+
$class = StateMachineValidator::class;
63+
} elseif ($tag['marking_store'] === 'scalar') {
64+
$name = 'single_place';
65+
$class = SinglePlaceWorkflowValidator::class;
66+
} else {
67+
$name = 'workflow';
68+
$class = WorkflowValidator::class;
69+
}
70+
71+
if (empty($this->validators[$name])) {
72+
$this->validators[$name] = new $class();
73+
}
74+
75+
return $this->validators[$name];
76+
}
77+
}

DependencyInjection/Configuration.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,10 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode)
236236
->useAttributeAsKey('name')
237237
->prototype('array')
238238
->children()
239+
->enumNode('type')
240+
->values(array('workflow', 'state_machine'))
241+
->defaultValue('workflow')
242+
->end()
239243
->arrayNode('marking_store')
240244
->isRequired()
241245
->children()

DependencyInjection/FrameworkExtension.php

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -404,10 +404,20 @@ private function registerWorkflowConfiguration(array $workflows, ContainerBuilde
404404
$registryDefinition = $container->getDefinition('workflow.registry');
405405

406406
foreach ($workflows as $name => $workflow) {
407+
$type = $workflow['type'];
408+
407409
$definitionDefinition = new Definition(Workflow\Definition::class);
408410
$definitionDefinition->addMethodCall('addPlaces', array($workflow['places']));
409411
foreach ($workflow['transitions'] as $transitionName => $transition) {
410-
$definitionDefinition->addMethodCall('addTransition', array(new Definition(Workflow\Transition::class, array($transitionName, $transition['from'], $transition['to']))));
412+
if ($type === 'workflow') {
413+
$definitionDefinition->addMethodCall('addTransition', array(new Definition(Workflow\Transition::class, array($transitionName, $transition['from'], $transition['to']))));
414+
} elseif ($type === 'state_machine') {
415+
foreach ($transition['from'] as $from) {
416+
foreach ($transition['to'] as $to) {
417+
$definitionDefinition->addMethodCall('addTransition', array(new Definition(Workflow\Transition::class, array($transitionName, $from, $to))));
418+
}
419+
}
420+
}
411421
}
412422

413423
if (isset($workflow['marking_store']['type'])) {
@@ -419,13 +429,21 @@ private function registerWorkflowConfiguration(array $workflows, ContainerBuilde
419429
$markingStoreDefinition = new Reference($workflow['marking_store']['service']);
420430
}
421431

422-
$workflowDefinition = new DefinitionDecorator('workflow.abstract');
432+
$definitionDefinition->addTag('workflow.definition', array(
433+
'name' => $name,
434+
'type' => $type,
435+
'marking_store' => isset($workflow['marking_store']['type']) ? $workflow['marking_store']['type'] : null,
436+
));
437+
$definitionDefinition->setPublic(false);
438+
439+
$workflowDefinition = new DefinitionDecorator(sprintf('%s.abstract', $type));
423440
$workflowDefinition->replaceArgument(0, $definitionDefinition);
424441
$workflowDefinition->replaceArgument(1, $markingStoreDefinition);
425442
$workflowDefinition->replaceArgument(3, $name);
426443

427-
$workflowId = 'workflow.'.$name;
444+
$workflowId = sprintf('%s.%s', $type, $name);
428445

446+
$container->setDefinition(sprintf('%s.definition', $workflowId), $definitionDefinition);
429447
$container->setDefinition($workflowId, $workflowDefinition);
430448

431449
foreach ($workflow['supports'] as $supportedClass) {

FrameworkBundle.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\SerializerPass;
3636
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\UnusedTagsPass;
3737
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ConfigCachePass;
38+
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ValidateWorkflowsPass;
3839
use Symfony\Component\Debug\ErrorHandler;
3940
use Symfony\Component\DependencyInjection\ContainerBuilder;
4041
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
@@ -93,6 +94,7 @@ public function build(ContainerBuilder $container)
9394
$container->addCompilerPass(new PropertyInfoPass());
9495
$container->addCompilerPass(new ControllerArgumentValueResolverPass());
9596
$container->addCompilerPass(new CachePoolPass());
97+
$container->addCompilerPass(new ValidateWorkflowsPass());
9698
$container->addCompilerPass(new CachePoolClearerPass(), PassConfig::TYPE_AFTER_REMOVING);
9799

98100
if ($container->getParameter('kernel.debug')) {

Resources/config/workflow.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@
1111
<argument type="service" id="event_dispatcher" on-invalid="ignore" />
1212
<argument /> <!-- name -->
1313
</service>
14+
<service id="state_machine.abstract" class="Symfony\Component\Workflow\StateMachine" abstract="true">
15+
<argument /> <!-- workflow definition -->
16+
<argument /> <!-- marking store -->
17+
<argument type="service" id="event_dispatcher" on-invalid="ignore" />
18+
<argument /> <!-- name -->
19+
</service>
1420

1521
<service id="workflow.marking_store.property_accessor" class="Symfony\Component\Workflow\MarkingStore\PropertyAccessorMarkingStore" abstract="true" />
1622
<service id="workflow.marking_store.scalar" class="Symfony\Component\Workflow\MarkingStore\ScalarMarkingStore" abstract="true" />

0 commit comments

Comments
 (0)