Added support for many inital places
This commit is contained in:
parent
fc826aac4c
commit
1af1bf29ef
|
@ -127,6 +127,27 @@ Security
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Workflow
|
||||||
|
--------
|
||||||
|
|
||||||
|
* `initial_place` is deprecated in favour of `initial_places`.
|
||||||
|
|
||||||
|
Before:
|
||||||
|
```yaml
|
||||||
|
framework:
|
||||||
|
workflows:
|
||||||
|
article:
|
||||||
|
initial_place: draft
|
||||||
|
```
|
||||||
|
|
||||||
|
After:
|
||||||
|
```yaml
|
||||||
|
framework:
|
||||||
|
workflows:
|
||||||
|
article:
|
||||||
|
initial_places: [draft]
|
||||||
|
```
|
||||||
|
|
||||||
Yaml
|
Yaml
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
|
@ -368,6 +368,7 @@ Workflow
|
||||||
* `SupportStrategyInterface` has been removed, use `WorkflowSupportStrategyInterface` instead.
|
* `SupportStrategyInterface` has been removed, use `WorkflowSupportStrategyInterface` instead.
|
||||||
* `ClassInstanceSupportStrategy` has been removed, use `InstanceOfSupportStrategy` instead.
|
* `ClassInstanceSupportStrategy` has been removed, use `InstanceOfSupportStrategy` instead.
|
||||||
* `MarkingStoreInterface::setMarking()` has a third argument: `array $context = []`.
|
* `MarkingStoreInterface::setMarking()` has a third argument: `array $context = []`.
|
||||||
|
* Removed support of `initial_place`. Use `initial_places` instead.
|
||||||
|
|
||||||
Yaml
|
Yaml
|
||||||
----
|
----
|
||||||
|
|
|
@ -231,7 +231,7 @@ class Configuration implements ConfigurationInterface
|
||||||
$workflows = [];
|
$workflows = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (1 === \count($workflows) && isset($workflows['workflows']) && array_keys($workflows['workflows']) !== range(0, \count($workflows) - 1) && !empty(array_diff(array_keys($workflows['workflows']), ['audit_trail', 'type', 'marking_store', 'supports', 'support_strategy', 'initial_place', 'places', 'transitions']))) {
|
if (1 === \count($workflows) && isset($workflows['workflows']) && array_keys($workflows['workflows']) !== range(0, \count($workflows) - 1) && !empty(array_diff(array_keys($workflows['workflows']), ['audit_trail', 'type', 'marking_store', 'supports', 'support_strategy', 'initial_places', 'places', 'transitions']))) {
|
||||||
$workflows = $workflows['workflows'];
|
$workflows = $workflows['workflows'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,6 +258,7 @@ class Configuration implements ConfigurationInterface
|
||||||
->prototype('array')
|
->prototype('array')
|
||||||
->fixXmlConfig('support')
|
->fixXmlConfig('support')
|
||||||
->fixXmlConfig('place')
|
->fixXmlConfig('place')
|
||||||
|
->fixXmlConfig('initial_place')
|
||||||
->fixXmlConfig('transition')
|
->fixXmlConfig('transition')
|
||||||
->children()
|
->children()
|
||||||
->arrayNode('audit_trail')
|
->arrayNode('audit_trail')
|
||||||
|
@ -312,8 +313,17 @@ class Configuration implements ConfigurationInterface
|
||||||
->cannotBeEmpty()
|
->cannotBeEmpty()
|
||||||
->end()
|
->end()
|
||||||
->scalarNode('initial_place')
|
->scalarNode('initial_place')
|
||||||
|
->setDeprecated('The "%path%.%node%" configuration key has been deprecated in Symfony 4.3, use the "initial_places" configuration key instead.')
|
||||||
->defaultNull()
|
->defaultNull()
|
||||||
->end()
|
->end()
|
||||||
|
->arrayNode('initial_places')
|
||||||
|
->beforeNormalization()
|
||||||
|
->ifTrue(function ($v) { return !\is_array($v); })
|
||||||
|
->then(function ($v) { return [$v]; })
|
||||||
|
->end()
|
||||||
|
->defaultValue([])
|
||||||
|
->prototype('scalar')->end()
|
||||||
|
->end()
|
||||||
->arrayNode('places')
|
->arrayNode('places')
|
||||||
->beforeNormalization()
|
->beforeNormalization()
|
||||||
->always()
|
->always()
|
||||||
|
|
|
@ -615,14 +615,14 @@ class FrameworkExtension extends Extension
|
||||||
|
|
||||||
// Create places
|
// Create places
|
||||||
$places = array_column($workflow['places'], 'name');
|
$places = array_column($workflow['places'], 'name');
|
||||||
$initialPlace = $workflow['initial_place'] ?? null;
|
$initialPlaces = $workflow['initial_places'] ?? $workflow['initial_place'] ?? [];
|
||||||
|
|
||||||
// Create a Definition
|
// Create a Definition
|
||||||
$definitionDefinition = new Definition(Workflow\Definition::class);
|
$definitionDefinition = new Definition(Workflow\Definition::class);
|
||||||
$definitionDefinition->setPublic(false);
|
$definitionDefinition->setPublic(false);
|
||||||
$definitionDefinition->addArgument($places);
|
$definitionDefinition->addArgument($places);
|
||||||
$definitionDefinition->addArgument($transitions);
|
$definitionDefinition->addArgument($transitions);
|
||||||
$definitionDefinition->addArgument($initialPlace);
|
$definitionDefinition->addArgument($initialPlaces);
|
||||||
$definitionDefinition->addArgument($metadataStoreDefinition);
|
$definitionDefinition->addArgument($metadataStoreDefinition);
|
||||||
|
|
||||||
// Create MarkingStore
|
// Create MarkingStore
|
||||||
|
@ -670,7 +670,7 @@ class FrameworkExtension extends Extension
|
||||||
->addTransitions(array_map(function (Reference $ref) use ($container): Workflow\Transition {
|
->addTransitions(array_map(function (Reference $ref) use ($container): Workflow\Transition {
|
||||||
return $container->get((string) $ref);
|
return $container->get((string) $ref);
|
||||||
}, $transitions))
|
}, $transitions))
|
||||||
->setInitialPlace($initialPlace)
|
->setInitialPlace($initialPlaces)
|
||||||
->build()
|
->build()
|
||||||
;
|
;
|
||||||
$validator->validate($realDefinition, $name);
|
$validator->validate($realDefinition, $name);
|
||||||
|
|
|
@ -270,6 +270,7 @@
|
||||||
|
|
||||||
<xsd:complexType name="workflow">
|
<xsd:complexType name="workflow">
|
||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
|
<xsd:element name="initial-place" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
|
||||||
<xsd:element name="marking-store" type="marking_store" minOccurs="0" maxOccurs="1" />
|
<xsd:element name="marking-store" type="marking_store" minOccurs="0" maxOccurs="1" />
|
||||||
<xsd:element name="support" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
|
<xsd:element name="support" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
|
||||||
<xsd:element name="place" type="place" minOccurs="0" maxOccurs="unbounded" />
|
<xsd:element name="place" type="place" minOccurs="0" maxOccurs="unbounded" />
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest;
|
||||||
|
|
||||||
|
$container->loadFromExtension('framework', [
|
||||||
|
'workflows' => [
|
||||||
|
'legacy' => [
|
||||||
|
'type' => 'workflow',
|
||||||
|
'supports' => [
|
||||||
|
stdClass::class,
|
||||||
|
],
|
||||||
|
'initial_place' => 'draft',
|
||||||
|
'places' => [
|
||||||
|
'draft',
|
||||||
|
'published',
|
||||||
|
],
|
||||||
|
'transitions' => [
|
||||||
|
'publish' => [
|
||||||
|
'from' => 'draft',
|
||||||
|
'to' => 'published',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
|
@ -12,7 +12,7 @@ $container->loadFromExtension('framework', [
|
||||||
'supports' => [
|
'supports' => [
|
||||||
FrameworkExtensionTest::class,
|
FrameworkExtensionTest::class,
|
||||||
],
|
],
|
||||||
'initial_place' => 'draft',
|
'initial_places' => ['draft'],
|
||||||
'places' => [
|
'places' => [
|
||||||
'draft',
|
'draft',
|
||||||
'wait_for_journalist',
|
'wait_for_journalist',
|
||||||
|
|
|
@ -12,7 +12,7 @@ $container->loadFromExtension('framework', [
|
||||||
'supports' => [
|
'supports' => [
|
||||||
FrameworkExtensionTest::class,
|
FrameworkExtensionTest::class,
|
||||||
],
|
],
|
||||||
'initial_place' => 'draft',
|
'initial_places' => ['draft'],
|
||||||
'places' => [
|
'places' => [
|
||||||
'draft',
|
'draft',
|
||||||
'wait_for_journalist',
|
'wait_for_journalist',
|
||||||
|
|
|
@ -12,7 +12,7 @@ $container->loadFromExtension('framework', [
|
||||||
'supports' => [
|
'supports' => [
|
||||||
FrameworkExtensionTest::class,
|
FrameworkExtensionTest::class,
|
||||||
],
|
],
|
||||||
'initial_place' => 'draft',
|
'initial_places' => ['draft'],
|
||||||
'places' => [
|
'places' => [
|
||||||
'draft',
|
'draft',
|
||||||
'wait_for_journalist',
|
'wait_for_journalist',
|
||||||
|
@ -47,7 +47,7 @@ $container->loadFromExtension('framework', [
|
||||||
'supports' => [
|
'supports' => [
|
||||||
FrameworkExtensionTest::class,
|
FrameworkExtensionTest::class,
|
||||||
],
|
],
|
||||||
'initial_place' => 'start',
|
'initial_places' => ['start'],
|
||||||
'metadata' => [
|
'metadata' => [
|
||||||
'title' => 'workflow title',
|
'title' => 'workflow title',
|
||||||
],
|
],
|
||||||
|
|
|
@ -6,7 +6,7 @@ $container->loadFromExtension('framework', [
|
||||||
'foo' => [
|
'foo' => [
|
||||||
'type' => 'workflow',
|
'type' => 'workflow',
|
||||||
'supports' => ['Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest'],
|
'supports' => ['Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest'],
|
||||||
'initial_place' => 'bar',
|
'initial_places' => ['bar'],
|
||||||
'places' => ['bar', 'baz'],
|
'places' => ['bar', 'baz'],
|
||||||
'transitions' => [
|
'transitions' => [
|
||||||
'bar_baz' => [
|
'bar_baz' => [
|
||||||
|
|
|
@ -6,7 +6,7 @@ $container->loadFromExtension('framework', [
|
||||||
'workflows' => [
|
'workflows' => [
|
||||||
'type' => 'workflow',
|
'type' => 'workflow',
|
||||||
'supports' => ['Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest'],
|
'supports' => ['Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest'],
|
||||||
'initial_place' => 'bar',
|
'initial_places' => ['bar'],
|
||||||
'places' => ['bar', 'baz'],
|
'places' => ['bar', 'baz'],
|
||||||
'transitions' => [
|
'transitions' => [
|
||||||
'bar_baz' => [
|
'bar_baz' => [
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" ?>
|
||||||
|
|
||||||
|
<container xmlns="http://symfony.com/schema/dic/services"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:framework="http://symfony.com/schema/dic/symfony"
|
||||||
|
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
|
||||||
|
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
|
||||||
|
|
||||||
|
<framework:config>
|
||||||
|
<framework:workflow name="legacy" type="workflow" initial-place="draft">
|
||||||
|
<framework:support>stdClass</framework:support>
|
||||||
|
<framework:place name="draft"></framework:place>
|
||||||
|
<framework:place name="published"></framework:place>
|
||||||
|
<framework:transition name="publish">
|
||||||
|
<framework:from>draft</framework:from>
|
||||||
|
<framework:to>published</framework:to>
|
||||||
|
</framework:transition>
|
||||||
|
</framework:workflow>
|
||||||
|
</framework:config>
|
||||||
|
</container>
|
|
@ -7,7 +7,8 @@
|
||||||
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
|
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
|
||||||
|
|
||||||
<framework:config>
|
<framework:config>
|
||||||
<framework:workflow name="article" type="workflow" initial-place="draft">
|
<framework:workflow name="article" type="workflow">
|
||||||
|
<framework:initial-place>draft</framework:initial-place>
|
||||||
<framework:marking-store type="multiple_state">
|
<framework:marking-store type="multiple_state">
|
||||||
<framework:argument>a</framework:argument>
|
<framework:argument>a</framework:argument>
|
||||||
<framework:argument>a</framework:argument>
|
<framework:argument>a</framework:argument>
|
||||||
|
|
|
@ -7,7 +7,8 @@
|
||||||
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
|
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
|
||||||
|
|
||||||
<framework:config>
|
<framework:config>
|
||||||
<framework:workflow name="article" type="workflow" initial-place="draft">
|
<framework:workflow name="article" type="workflow">
|
||||||
|
<framework:initial-place>draft</framework:initial-place>
|
||||||
<framework:marking-store type="multiple_state">
|
<framework:marking-store type="multiple_state">
|
||||||
<framework:argument>a</framework:argument>
|
<framework:argument>a</framework:argument>
|
||||||
<framework:argument>a</framework:argument>
|
<framework:argument>a</framework:argument>
|
||||||
|
|
|
@ -7,7 +7,8 @@
|
||||||
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
|
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
|
||||||
|
|
||||||
<framework:config>
|
<framework:config>
|
||||||
<framework:workflow name="article" type="workflow" initial-place="draft">
|
<framework:workflow name="article" type="workflow">
|
||||||
|
<framework:initial-place>draft</framework:initial-place>
|
||||||
<framework:marking-store type="multiple_state">
|
<framework:marking-store type="multiple_state">
|
||||||
<framework:argument>a</framework:argument>
|
<framework:argument>a</framework:argument>
|
||||||
<framework:argument>a</framework:argument>
|
<framework:argument>a</framework:argument>
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
|
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
|
||||||
|
|
||||||
<framework:config>
|
<framework:config>
|
||||||
<framework:workflow enabled="true" name="foo" type="workflow" initial-place="bar">
|
<framework:workflow enabled="true" name="foo" type="workflow">
|
||||||
|
<framework:initial-place>bar</framework:initial-place>
|
||||||
<framework:support>Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest</framework:support>
|
<framework:support>Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest</framework:support>
|
||||||
<framework:place>bar</framework:place>
|
<framework:place>bar</framework:place>
|
||||||
<framework:place>baz</framework:place>
|
<framework:place>baz</framework:place>
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
|
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
|
||||||
|
|
||||||
<framework:config>
|
<framework:config>
|
||||||
<framework:workflow enabled="true" name="workflows" type="workflow" initial-place="bar">
|
<framework:workflow enabled="true" name="workflows" type="workflow">
|
||||||
|
<framework:initial-place>bar</framework:initial-place>
|
||||||
<framework:support>Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest</framework:support>
|
<framework:support>Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest</framework:support>
|
||||||
<framework:place>bar</framework:place>
|
<framework:place>bar</framework:place>
|
||||||
<framework:place>baz</framework:place>
|
<framework:place>baz</framework:place>
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
framework:
|
||||||
|
workflows:
|
||||||
|
legacy:
|
||||||
|
type: workflow
|
||||||
|
initial_place: draft
|
||||||
|
supports:
|
||||||
|
- stdClass
|
||||||
|
places:
|
||||||
|
- draft
|
||||||
|
- published
|
||||||
|
transitions:
|
||||||
|
publish:
|
||||||
|
from: draft
|
||||||
|
to: published
|
|
@ -6,7 +6,7 @@ framework:
|
||||||
type: multiple_state
|
type: multiple_state
|
||||||
supports:
|
supports:
|
||||||
- Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest
|
- Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest
|
||||||
initial_place: draft
|
initial_places: [draft]
|
||||||
places:
|
places:
|
||||||
- draft
|
- draft
|
||||||
- wait_for_journalist
|
- wait_for_journalist
|
||||||
|
|
|
@ -6,7 +6,7 @@ framework:
|
||||||
type: multiple_state
|
type: multiple_state
|
||||||
supports:
|
supports:
|
||||||
- Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest
|
- Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest
|
||||||
initial_place: draft
|
initial_places: [draft]
|
||||||
places:
|
places:
|
||||||
- draft
|
- draft
|
||||||
- wait_for_journalist
|
- wait_for_journalist
|
||||||
|
|
|
@ -6,7 +6,7 @@ framework:
|
||||||
type: multiple_state
|
type: multiple_state
|
||||||
supports:
|
supports:
|
||||||
- Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest
|
- Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest
|
||||||
initial_place: draft
|
initial_places: [draft]
|
||||||
places:
|
places:
|
||||||
# simple format
|
# simple format
|
||||||
- draft
|
- draft
|
||||||
|
@ -33,7 +33,7 @@ framework:
|
||||||
type: single_state
|
type: single_state
|
||||||
supports:
|
supports:
|
||||||
- Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest
|
- Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest
|
||||||
initial_place: start
|
initial_places: [start]
|
||||||
metadata:
|
metadata:
|
||||||
title: workflow title
|
title: workflow title
|
||||||
places:
|
places:
|
||||||
|
|
|
@ -6,7 +6,7 @@ framework:
|
||||||
type: workflow
|
type: workflow
|
||||||
supports:
|
supports:
|
||||||
- Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest
|
- Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest
|
||||||
initial_place: bar
|
initial_places: [bar]
|
||||||
places:
|
places:
|
||||||
- bar
|
- bar
|
||||||
- baz
|
- baz
|
||||||
|
|
|
@ -5,7 +5,7 @@ framework:
|
||||||
type: workflow
|
type: workflow
|
||||||
supports:
|
supports:
|
||||||
- Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest
|
- Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest
|
||||||
initial_place: bar
|
initial_places: [bar]
|
||||||
places:
|
places:
|
||||||
- bar
|
- bar
|
||||||
- baz
|
- baz
|
||||||
|
|
|
@ -217,7 +217,7 @@ abstract class FrameworkExtensionTest extends TestCase
|
||||||
'Places are passed to the workflow definition'
|
'Places are passed to the workflow definition'
|
||||||
);
|
);
|
||||||
$this->assertCount(4, $workflowDefinition->getArgument(1));
|
$this->assertCount(4, $workflowDefinition->getArgument(1));
|
||||||
$this->assertSame('draft', $workflowDefinition->getArgument(2));
|
$this->assertSame(['draft'], $workflowDefinition->getArgument(2));
|
||||||
|
|
||||||
$this->assertTrue($container->hasDefinition('state_machine.pull_request'), 'State machine is registered as a service');
|
$this->assertTrue($container->hasDefinition('state_machine.pull_request'), 'State machine is registered as a service');
|
||||||
$this->assertSame('state_machine.abstract', $container->getDefinition('state_machine.pull_request')->getParent());
|
$this->assertSame('state_machine.abstract', $container->getDefinition('state_machine.pull_request')->getParent());
|
||||||
|
@ -238,7 +238,7 @@ abstract class FrameworkExtensionTest extends TestCase
|
||||||
'Places are passed to the state machine definition'
|
'Places are passed to the state machine definition'
|
||||||
);
|
);
|
||||||
$this->assertCount(9, $stateMachineDefinition->getArgument(1));
|
$this->assertCount(9, $stateMachineDefinition->getArgument(1));
|
||||||
$this->assertSame('start', $stateMachineDefinition->getArgument(2));
|
$this->assertSame(['start'], $stateMachineDefinition->getArgument(2));
|
||||||
|
|
||||||
$metadataStoreDefinition = $stateMachineDefinition->getArgument(3);
|
$metadataStoreDefinition = $stateMachineDefinition->getArgument(3);
|
||||||
$this->assertInstanceOf(Definition::class, $metadataStoreDefinition);
|
$this->assertInstanceOf(Definition::class, $metadataStoreDefinition);
|
||||||
|
@ -271,6 +271,28 @@ abstract class FrameworkExtensionTest extends TestCase
|
||||||
$this->assertGreaterThan(0, \count($registryDefinition->getMethodCalls()));
|
$this->assertGreaterThan(0, \count($registryDefinition->getMethodCalls()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testWorkflowLegacy()
|
||||||
|
{
|
||||||
|
$container = $this->createContainerFromFile('workflow-legacy');
|
||||||
|
|
||||||
|
$this->assertTrue($container->hasDefinition('workflow.legacy'), 'Workflow is registered as a service');
|
||||||
|
$this->assertSame('workflow.abstract', $container->getDefinition('workflow.legacy')->getParent());
|
||||||
|
$this->assertTrue($container->hasDefinition('workflow.legacy.definition'), 'Workflow definition is registered as a service');
|
||||||
|
|
||||||
|
$workflowDefinition = $container->getDefinition('workflow.legacy.definition');
|
||||||
|
|
||||||
|
$this->assertSame(['draft'], $workflowDefinition->getArgument(2));
|
||||||
|
|
||||||
|
$this->assertSame(
|
||||||
|
[
|
||||||
|
'draft',
|
||||||
|
'published',
|
||||||
|
],
|
||||||
|
$workflowDefinition->getArgument(0),
|
||||||
|
'Places are passed to the workflow definition'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \Symfony\Component\Workflow\Exception\InvalidDefinitionException
|
* @expectedException \Symfony\Component\Workflow\Exception\InvalidDefinitionException
|
||||||
* @expectedExceptionMessage A transition from a place/state must have an unique name. Multiple transitions named "go" from place/state "first" where found on StateMachine "my_workflow".
|
* @expectedExceptionMessage A transition from a place/state must have an unique name. Multiple transitions named "go" from place/state "first" where found on StateMachine "my_workflow".
|
||||||
|
|
|
@ -54,7 +54,7 @@
|
||||||
"symfony/twig-bundle": "~2.8|~3.2|~4.0",
|
"symfony/twig-bundle": "~2.8|~3.2|~4.0",
|
||||||
"symfony/validator": "^4.1",
|
"symfony/validator": "^4.1",
|
||||||
"symfony/var-dumper": "~3.4|~4.0",
|
"symfony/var-dumper": "~3.4|~4.0",
|
||||||
"symfony/workflow": "^4.1",
|
"symfony/workflow": "^4.3",
|
||||||
"symfony/yaml": "~3.4|~4.0",
|
"symfony/yaml": "~3.4|~4.0",
|
||||||
"symfony/property-info": "~3.4|~4.0",
|
"symfony/property-info": "~3.4|~4.0",
|
||||||
"symfony/lock": "~3.4|~4.0",
|
"symfony/lock": "~3.4|~4.0",
|
||||||
|
@ -79,7 +79,7 @@
|
||||||
"symfony/translation": "<4.3",
|
"symfony/translation": "<4.3",
|
||||||
"symfony/twig-bridge": "<4.1.1",
|
"symfony/twig-bridge": "<4.1.1",
|
||||||
"symfony/validator": "<4.1",
|
"symfony/validator": "<4.1",
|
||||||
"symfony/workflow": "<4.1"
|
"symfony/workflow": "<4.3"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"ext-apcu": "For best performance of the system caches",
|
"ext-apcu": "For best performance of the system caches",
|
||||||
|
|
|
@ -26,6 +26,7 @@ CHANGELOG
|
||||||
* Dispatch `EnteredEvent` on `workflow.entered`
|
* Dispatch `EnteredEvent` on `workflow.entered`
|
||||||
* Dispatch `CompletedEvent` on `workflow.completed`
|
* Dispatch `CompletedEvent` on `workflow.completed`
|
||||||
* Dispatch `AnnounceEvent` on `workflow.announce`
|
* Dispatch `AnnounceEvent` on `workflow.announce`
|
||||||
|
* Added support for many `initialPlaces`
|
||||||
|
|
||||||
4.1.0
|
4.1.0
|
||||||
-----
|
-----
|
||||||
|
|
|
@ -24,14 +24,15 @@ final class Definition
|
||||||
{
|
{
|
||||||
private $places = [];
|
private $places = [];
|
||||||
private $transitions = [];
|
private $transitions = [];
|
||||||
private $initialPlace;
|
private $initialPlaces = [];
|
||||||
private $metadataStore;
|
private $metadataStore;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string[] $places
|
* @param string[] $places
|
||||||
* @param Transition[] $transitions
|
* @param Transition[] $transitions
|
||||||
|
* @param string|string[]|null $initialPlaces
|
||||||
*/
|
*/
|
||||||
public function __construct(array $places, array $transitions, string $initialPlace = null, MetadataStoreInterface $metadataStore = null)
|
public function __construct(array $places, array $transitions, $initialPlaces = null, MetadataStoreInterface $metadataStore = null)
|
||||||
{
|
{
|
||||||
foreach ($places as $place) {
|
foreach ($places as $place) {
|
||||||
$this->addPlace($place);
|
$this->addPlace($place);
|
||||||
|
@ -41,17 +42,33 @@ final class Definition
|
||||||
$this->addTransition($transition);
|
$this->addTransition($transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->setInitialPlace($initialPlace);
|
$this->setInitialPlaces($initialPlaces);
|
||||||
|
|
||||||
$this->metadataStore = $metadataStore ?: new InMemoryMetadataStore();
|
$this->metadataStore = $metadataStore ?: new InMemoryMetadataStore();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @deprecated since Symfony 4.3. Use the getInitialPlaces() instead.
|
||||||
|
*
|
||||||
* @return string|null
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
public function getInitialPlace()
|
public function getInitialPlace()
|
||||||
{
|
{
|
||||||
return $this->initialPlace;
|
@trigger_error(sprintf('Calling %s::getInitialPlace() is deprecated. Call %s::getInitialPlaces() instead.', __CLASS__, __CLASS__));
|
||||||
|
|
||||||
|
if (!$this->initialPlaces) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return reset($this->initialPlaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public function getInitialPlaces(): array
|
||||||
|
{
|
||||||
|
return $this->initialPlaces;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -75,23 +92,27 @@ final class Definition
|
||||||
return $this->metadataStore;
|
return $this->metadataStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function setInitialPlace(string $place = null)
|
private function setInitialPlaces($places = null)
|
||||||
{
|
{
|
||||||
if (null === $place) {
|
if (null === $places) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$places = (array) $places;
|
||||||
|
|
||||||
|
foreach ($places as $place) {
|
||||||
if (!isset($this->places[$place])) {
|
if (!isset($this->places[$place])) {
|
||||||
throw new LogicException(sprintf('Place "%s" cannot be the initial place as it does not exist.', $place));
|
throw new LogicException(sprintf('Place "%s" cannot be the initial place as it does not exist.', $place));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->initialPlace = $place;
|
$this->initialPlaces = $places;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function addPlace(string $place)
|
private function addPlace(string $place)
|
||||||
{
|
{
|
||||||
if (!\count($this->places)) {
|
if (!\count($this->places)) {
|
||||||
$this->initialPlace = $place;
|
$this->initialPlaces = [$place];
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->places[$place] = $place;
|
$this->places[$place] = $place;
|
||||||
|
|
|
@ -69,7 +69,7 @@ class GraphvizDumper implements DumperInterface
|
||||||
|
|
||||||
foreach ($definition->getPlaces() as $place) {
|
foreach ($definition->getPlaces() as $place) {
|
||||||
$attributes = [];
|
$attributes = [];
|
||||||
if ($place === $definition->getInitialPlace()) {
|
if (\in_array($place, $definition->getInitialPlaces(), true)) {
|
||||||
$attributes['style'] = 'filled';
|
$attributes['style'] = 'filled';
|
||||||
}
|
}
|
||||||
if ($marking && $marking->has($place)) {
|
if ($marking && $marking->has($place)) {
|
||||||
|
|
|
@ -199,7 +199,7 @@ class PlantUmlDumper implements DumperInterface
|
||||||
$placeEscaped = $this->escape($place);
|
$placeEscaped = $this->escape($place);
|
||||||
|
|
||||||
$output = "state $placeEscaped".
|
$output = "state $placeEscaped".
|
||||||
($definition->getInitialPlace() === $place ? ' '.self::INITIAL : '').
|
(\in_array($place, $definition->getInitialPlaces(), true) ? ' '.self::INITIAL : '').
|
||||||
($marking && $marking->has($place) ? ' '.self::MARKED : '');
|
($marking && $marking->has($place) ? ' '.self::MARKED : '');
|
||||||
|
|
||||||
$backgroundColor = $workflowMetadata->getMetadata('bg_color', $place);
|
$backgroundColor = $workflowMetadata->getMetadata('bg_color', $place);
|
||||||
|
|
|
@ -15,7 +15,7 @@ class DefinitionBuilderTest extends TestCase
|
||||||
$builder->setInitialPlace('b');
|
$builder->setInitialPlace('b');
|
||||||
$definition = $builder->build();
|
$definition = $builder->build();
|
||||||
|
|
||||||
$this->assertEquals('b', $definition->getInitialPlace());
|
$this->assertEquals(['b'], $definition->getInitialPlaces());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAddTransition()
|
public function testAddTransition()
|
||||||
|
|
|
@ -15,7 +15,7 @@ class DefinitionTest extends TestCase
|
||||||
|
|
||||||
$this->assertCount(5, $definition->getPlaces());
|
$this->assertCount(5, $definition->getPlaces());
|
||||||
|
|
||||||
$this->assertEquals('a', $definition->getInitialPlace());
|
$this->assertEquals(['a'], $definition->getInitialPlaces());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSetInitialPlace()
|
public function testSetInitialPlace()
|
||||||
|
@ -23,7 +23,15 @@ class DefinitionTest extends TestCase
|
||||||
$places = range('a', 'e');
|
$places = range('a', 'e');
|
||||||
$definition = new Definition($places, [], $places[3]);
|
$definition = new Definition($places, [], $places[3]);
|
||||||
|
|
||||||
$this->assertEquals($places[3], $definition->getInitialPlace());
|
$this->assertEquals([$places[3]], $definition->getInitialPlaces());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetInitialPlaces()
|
||||||
|
{
|
||||||
|
$places = range('a', 'e');
|
||||||
|
$definition = new Definition($places, [], ['a', 'e']);
|
||||||
|
|
||||||
|
$this->assertEquals(['a', 'e'], $definition->getInitialPlaces());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -109,4 +109,32 @@ class StateMachineValidatorTest extends TestCase
|
||||||
// | t2 | --> | c |
|
// | t2 | --> | c |
|
||||||
// +----+ +----+
|
// +----+ +----+
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \Symfony\Component\Workflow\Exception\InvalidDefinitionException
|
||||||
|
* @expectedExceptionMessage The state machine "foo" can not store many places. But the definition has 2 initial places. Only one is supported.
|
||||||
|
*/
|
||||||
|
public function testWithTooManyInitialPlaces()
|
||||||
|
{
|
||||||
|
$places = range('a', 'c');
|
||||||
|
$transitions = [];
|
||||||
|
$definition = new Definition($places, $transitions, ['a', 'b']);
|
||||||
|
|
||||||
|
(new StateMachineValidator())->validate($definition, 'foo');
|
||||||
|
|
||||||
|
// the test ensures that the validation does not fail (i.e. it does not throw any exceptions)
|
||||||
|
$this->addToAssertionCount(1);
|
||||||
|
|
||||||
|
// The graph looks like:
|
||||||
|
//
|
||||||
|
// +----+ +----+ +---+
|
||||||
|
// | a | --> | t1 | --> | b |
|
||||||
|
// +----+ +----+ +---+
|
||||||
|
// |
|
||||||
|
// |
|
||||||
|
// v
|
||||||
|
// +----+ +----+
|
||||||
|
// | t2 | --> | c |
|
||||||
|
// +----+ +----+
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,20 @@ class WorkflowValidatorTest extends TestCase
|
||||||
(new WorkflowValidator())->validate($definition, 'foo');
|
(new WorkflowValidator())->validate($definition, 'foo');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \Symfony\Component\Workflow\Exception\InvalidDefinitionException
|
||||||
|
* @expectedExceptionMessage The marking store of workflow "foo" can not store many places. But the definition has 2 initial places. Only one is supported.
|
||||||
|
*/
|
||||||
|
public function testWithTooManyInitialPlaces()
|
||||||
|
{
|
||||||
|
$places = range('a', 'c');
|
||||||
|
$transitions = [];
|
||||||
|
|
||||||
|
$definition = new Definition($places, $transitions, ['a', 'b']);
|
||||||
|
|
||||||
|
(new WorkflowValidator(true))->validate($definition, 'foo');
|
||||||
|
}
|
||||||
|
|
||||||
public function testSameTransitionNameButNotSamePlace()
|
public function testSameTransitionNameButNotSamePlace()
|
||||||
{
|
{
|
||||||
$places = range('a', 'd');
|
$places = range('a', 'd');
|
||||||
|
|
|
@ -16,6 +16,7 @@ use Symfony\Component\Workflow\Exception\InvalidDefinitionException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||||
|
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||||
*/
|
*/
|
||||||
interface DefinitionValidatorInterface
|
interface DefinitionValidatorInterface
|
||||||
{
|
{
|
||||||
|
|
|
@ -37,10 +37,15 @@ class StateMachineValidator implements DefinitionValidatorInterface
|
||||||
// Enforcing uniqueness of the names of transitions starting at each node
|
// Enforcing uniqueness of the names of transitions starting at each node
|
||||||
$from = reset($froms);
|
$from = reset($froms);
|
||||||
if (isset($transitionFromNames[$from][$transition->getName()])) {
|
if (isset($transitionFromNames[$from][$transition->getName()])) {
|
||||||
throw new InvalidDefinitionException(sprintf('A transition from a place/state must have an unique name. Multiple transitions named "%s" from place/state "%s" where found on StateMachine "%s". ', $transition->getName(), $from, $name));
|
throw new InvalidDefinitionException(sprintf('A transition from a place/state must have an unique name. Multiple transitions named "%s" from place/state "%s" where found on StateMachine "%s".', $transition->getName(), $from, $name));
|
||||||
}
|
}
|
||||||
|
|
||||||
$transitionFromNames[$from][$transition->getName()] = true;
|
$transitionFromNames[$from][$transition->getName()] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$initialPlaces = $definition->getInitialPlaces();
|
||||||
|
if (2 <= count($initialPlaces)) {
|
||||||
|
throw new InvalidDefinitionException(sprintf('The state machine "%s" can not store many places. But the definition has %s initial places. Only one is supported.', $name, \count($initialPlaces)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ use Symfony\Component\Workflow\Exception\InvalidDefinitionException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||||
|
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||||
*/
|
*/
|
||||||
class WorkflowValidator implements DefinitionValidatorInterface
|
class WorkflowValidator implements DefinitionValidatorInterface
|
||||||
{
|
{
|
||||||
|
@ -48,5 +49,10 @@ class WorkflowValidator implements DefinitionValidatorInterface
|
||||||
throw new InvalidDefinitionException(sprintf('The marking store of workflow "%s" can not store many places. But the transition "%s" has too many output (%d). Only one is accepted.', $name, $transition->getName(), \count($transition->getTos())));
|
throw new InvalidDefinitionException(sprintf('The marking store of workflow "%s" can not store many places. But the transition "%s" has too many output (%d). Only one is accepted.', $name, $transition->getName(), \count($transition->getTos())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$initialPlaces = $definition->getInitialPlaces();
|
||||||
|
if (2 <= count($initialPlaces)) {
|
||||||
|
throw new InvalidDefinitionException(sprintf('The marking store of workflow "%s" can not store many places. But the definition has %s initial places. Only one is supported.', $name, \count($initialPlaces)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,10 +61,12 @@ class Workflow implements WorkflowInterface
|
||||||
|
|
||||||
// check if the subject is already in the workflow
|
// check if the subject is already in the workflow
|
||||||
if (!$marking->getPlaces()) {
|
if (!$marking->getPlaces()) {
|
||||||
if (!$this->definition->getInitialPlace()) {
|
if (!$this->definition->getInitialPlaces()) {
|
||||||
throw new LogicException(sprintf('The Marking is empty and there is no initial place for workflow "%s".', $this->name));
|
throw new LogicException(sprintf('The Marking is empty and there is no initial place for workflow "%s".', $this->name));
|
||||||
}
|
}
|
||||||
$marking->mark($this->definition->getInitialPlace());
|
foreach ($this->definition->getInitialPlaces() as $place) {
|
||||||
|
$marking->mark($place);
|
||||||
|
}
|
||||||
|
|
||||||
// update the subject with the new marking
|
// update the subject with the new marking
|
||||||
$this->markingStore->setMarking($subject, $marking);
|
$this->markingStore->setMarking($subject, $marking);
|
||||||
|
|
Reference in New Issue