From 7d5b7a3392e2adbb9623642e10862f23915a768f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 8 Nov 2018 17:18:12 +0100 Subject: [PATCH] [Workflow] Added a context to `Workflow::apply()` --- .../DependencyInjection/Configuration.php | 2 +- .../FrameworkExtension.php | 3 +- .../Resources/config/workflow.xml | 1 + .../FrameworkExtensionTest.php | 4 +- src/Symfony/Component/Workflow/CHANGELOG.md | 1 + .../ValidateWorkflowsPass.php | 6 +- .../MarkingStore/MarkingStoreInterface.php | 5 +- .../MarkingStore/MethodMarkingStore.php | 83 +++++++++++++++++++ .../MultipleStateMarkingStore.php | 2 +- .../MarkingStore/SingleStateMarkingStore.php | 2 +- .../MarkingStore/MethodMarkingStoreTest.php | 74 +++++++++++++++++ src/Symfony/Component/Workflow/Workflow.php | 4 +- .../Component/Workflow/WorkflowInterface.php | 2 +- 13 files changed, 176 insertions(+), 13 deletions(-) create mode 100644 src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php create mode 100644 src/Symfony/Component/Workflow/Tests/MarkingStore/MethodMarkingStoreTest.php diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 421f669c952b0..2788cf019f5be 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -268,7 +268,7 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode) ->fixXmlConfig('argument') ->children() ->enumNode('type') - ->values(['multiple_state', 'single_state']) + ->values(['multiple_state', 'single_state', 'method']) ->end() ->arrayNode('arguments') ->beforeNormalization() diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 874aeaa9773b9..a38518b2e9229 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -614,7 +614,8 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ $definitionDefinition->addTag('workflow.definition', [ 'name' => $name, 'type' => $type, - 'marking_store' => isset($workflow['marking_store']['type']) ? $workflow['marking_store']['type'] : null, + 'marking_store' => $workflow['marking_store']['type'] ?? null, + 'single_state' => 'method' === ($workflow['marking_store']['type'] ?? null) && ($workflow['marking_store']['arguments'][0] ?? false), ]); // Create MarkingStore diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/workflow.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/workflow.xml index 5f349ea6a0a3c..0bba153b10317 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/workflow.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/workflow.xml @@ -22,6 +22,7 @@ + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index 586a3811e2277..b38439349ad3b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -215,7 +215,7 @@ public function testWorkflows() $workflowDefinition->getArgument(0), 'Places are passed to the workflow definition' ); - $this->assertSame(['workflow.definition' => [['name' => 'article', 'type' => 'workflow', 'marking_store' => 'multiple_state']]], $workflowDefinition->getTags()); + $this->assertSame(['workflow.definition' => [['name' => 'article', 'type' => 'workflow', 'marking_store' => 'multiple_state', 'single_state' => false]]], $workflowDefinition->getTags()); $this->assertCount(4, $workflowDefinition->getArgument(1)); $this->assertSame('draft', $workflowDefinition->getArgument(2)); @@ -237,7 +237,7 @@ public function testWorkflows() $stateMachineDefinition->getArgument(0), 'Places are passed to the state machine definition' ); - $this->assertSame(['workflow.definition' => [['name' => 'pull_request', 'type' => 'state_machine', 'marking_store' => 'single_state']]], $stateMachineDefinition->getTags()); + $this->assertSame(['workflow.definition' => [['name' => 'pull_request', 'type' => 'state_machine', 'marking_store' => 'single_state', 'single_state' => false]]], $stateMachineDefinition->getTags()); $this->assertCount(9, $stateMachineDefinition->getArgument(1)); $this->assertSame('start', $stateMachineDefinition->getArgument(2)); diff --git a/src/Symfony/Component/Workflow/CHANGELOG.md b/src/Symfony/Component/Workflow/CHANGELOG.md index 4668f2f68af17..7e54a59a61e8e 100644 --- a/src/Symfony/Component/Workflow/CHANGELOG.md +++ b/src/Symfony/Component/Workflow/CHANGELOG.md @@ -5,6 +5,7 @@ CHANGELOG ----- * Trigger `entered` event for subject entering in the Workflow for the first time + * Added a context to `Workflow::apply()`. The `MethodMarkingStore` could be used to leverage this feature. 4.1.0 ----- diff --git a/src/Symfony/Component/Workflow/DependencyInjection/ValidateWorkflowsPass.php b/src/Symfony/Component/Workflow/DependencyInjection/ValidateWorkflowsPass.php index 175a1f5b96d71..9294a1a7fb426 100644 --- a/src/Symfony/Component/Workflow/DependencyInjection/ValidateWorkflowsPass.php +++ b/src/Symfony/Component/Workflow/DependencyInjection/ValidateWorkflowsPass.php @@ -59,6 +59,10 @@ private function createValidator($tag) return new WorkflowValidator(true); } - return new WorkflowValidator(); + if ('multiple_state' === $tag['marking_store']) { + return new WorkflowValidator(false); + } + + return new WorkflowValidator($tag['single_state'] ?? false); } } diff --git a/src/Symfony/Component/Workflow/MarkingStore/MarkingStoreInterface.php b/src/Symfony/Component/Workflow/MarkingStore/MarkingStoreInterface.php index 76df9cc0d2160..991a2fc0bdad3 100644 --- a/src/Symfony/Component/Workflow/MarkingStore/MarkingStoreInterface.php +++ b/src/Symfony/Component/Workflow/MarkingStore/MarkingStoreInterface.php @@ -36,8 +36,7 @@ public function getMarking($subject); /** * Sets a Marking to a subject. * - * @param object $subject A subject - * @param Marking $marking A marking + * @param object $subject A subject */ - public function setMarking($subject, Marking $marking); + public function setMarking($subject, Marking $marking, array $context = []); } diff --git a/src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php b/src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php new file mode 100644 index 0000000000000..e2e61e7278d85 --- /dev/null +++ b/src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Workflow\MarkingStore; + +use Symfony\Component\Workflow\Exception\LogicException; +use Symfony\Component\Workflow\Marking; + +/** + * MethodMarkingStore stores the marking with a subject's method. + * + * This store deals with a "single state" or "multiple state" Marking. + * + * @author Grégoire Pineau + */ +class MethodMarkingStore implements MarkingStoreInterface +{ + private $singleState; + private $property; + + /** + * @param string $property Used to determine methods to call + * The `getMarking` method will use `$subject->getProperty()` + * The `setMarking` method will use `$subject->setProperty(string|array $places, array $context = array())` + */ + public function __construct(bool $singleState = false, string $property = 'marking') + { + $this->singleState = $singleState; + $this->property = $property; + } + + /** + * {@inheritdoc} + */ + public function getMarking($subject) + { + $method = 'get'.ucfirst($this->property); + + if (!method_exists($subject, $method)) { + throw new LogicException(sprintf('The method "%s::%s()" does not exists.', \get_class($subject), $method)); + } + + $marking = $subject->{$method}(); + + if (!$marking) { + return new Marking(); + } + + if ($this->singleState) { + $marking = [$marking => 1]; + } + + return new Marking($marking); + } + + /** + * {@inheritdoc} + */ + public function setMarking($subject, Marking $marking, array $context = []) + { + $marking = $marking->getPlaces(); + + if ($this->singleState) { + $marking = key($marking); + } + + $method = 'set'.ucfirst($this->property); + + if (!method_exists($subject, $method)) { + throw new LogicException(sprintf('The method "%s::%s()" does not exists.', \get_class($subject), $method)); + } + + $subject->{$method}($marking, $context); + } +} diff --git a/src/Symfony/Component/Workflow/MarkingStore/MultipleStateMarkingStore.php b/src/Symfony/Component/Workflow/MarkingStore/MultipleStateMarkingStore.php index 3ac0ff5827635..9e70614e02c34 100644 --- a/src/Symfony/Component/Workflow/MarkingStore/MultipleStateMarkingStore.php +++ b/src/Symfony/Component/Workflow/MarkingStore/MultipleStateMarkingStore.php @@ -46,7 +46,7 @@ public function getMarking($subject) /** * {@inheritdoc} */ - public function setMarking($subject, Marking $marking) + public function setMarking($subject, Marking $marking, array $context = []) { $this->propertyAccessor->setValue($subject, $this->property, $marking->getPlaces()); } diff --git a/src/Symfony/Component/Workflow/MarkingStore/SingleStateMarkingStore.php b/src/Symfony/Component/Workflow/MarkingStore/SingleStateMarkingStore.php index 806b33340caa2..ca28017384ae9 100644 --- a/src/Symfony/Component/Workflow/MarkingStore/SingleStateMarkingStore.php +++ b/src/Symfony/Component/Workflow/MarkingStore/SingleStateMarkingStore.php @@ -51,7 +51,7 @@ public function getMarking($subject) /** * {@inheritdoc} */ - public function setMarking($subject, Marking $marking) + public function setMarking($subject, Marking $marking, array $context = []) { $this->propertyAccessor->setValue($subject, $this->property, key($marking->getPlaces())); } diff --git a/src/Symfony/Component/Workflow/Tests/MarkingStore/MethodMarkingStoreTest.php b/src/Symfony/Component/Workflow/Tests/MarkingStore/MethodMarkingStoreTest.php new file mode 100644 index 0000000000000..776f66002d23c --- /dev/null +++ b/src/Symfony/Component/Workflow/Tests/MarkingStore/MethodMarkingStoreTest.php @@ -0,0 +1,74 @@ +getMarking($subject); + + $this->assertInstanceOf(Marking::class, $marking); + $this->assertCount(0, $marking->getPlaces()); + + $marking->mark('first_place'); + + $markingStore->setMarking($subject, $marking); + + $this->assertSame(['first_place' => 1], $subject->getMarking()); + + $marking2 = $markingStore->getMarking($subject); + + $this->assertEquals($marking, $marking2); + } + + public function testGetSetMarkingWithSingleState() + { + $subject = new Subject(); + + $markingStore = new MethodMarkingStore(true); + + $marking = $markingStore->getMarking($subject); + + $this->assertInstanceOf(Marking::class, $marking); + $this->assertCount(0, $marking->getPlaces()); + + $marking->mark('first_place'); + + $markingStore->setMarking($subject, $marking); + + $this->assertSame('first_place', $subject->getMarking()); + + $marking2 = $markingStore->getMarking($subject); + + $this->assertEquals($marking, $marking2); + } +} + +final class Subject +{ + private $marking; + + public function __construct($marking = null) + { + $this->marking = $marking; + } + + public function getMarking() + { + return $this->marking; + } + + public function setMarking($marking) + { + $this->marking = $marking; + } +} diff --git a/src/Symfony/Component/Workflow/Workflow.php b/src/Symfony/Component/Workflow/Workflow.php index ff229faabd314..7269defa2e11a 100644 --- a/src/Symfony/Component/Workflow/Workflow.php +++ b/src/Symfony/Component/Workflow/Workflow.php @@ -143,7 +143,7 @@ public function buildTransitionBlockerList($subject, string $transitionName): Tr /** * {@inheritdoc} */ - public function apply($subject, $transitionName) + public function apply($subject, $transitionName, array $context = []) { $marking = $this->getMarking($subject); @@ -172,7 +172,7 @@ public function apply($subject, $transitionName) $this->enter($subject, $transition, $marking); - $this->markingStore->setMarking($subject, $marking); + $this->markingStore->setMarking($subject, $marking, $context); $this->entered($subject, $transition, $marking); diff --git a/src/Symfony/Component/Workflow/WorkflowInterface.php b/src/Symfony/Component/Workflow/WorkflowInterface.php index 5a1f2c74e81aa..d6de18fee5794 100644 --- a/src/Symfony/Component/Workflow/WorkflowInterface.php +++ b/src/Symfony/Component/Workflow/WorkflowInterface.php @@ -58,7 +58,7 @@ public function buildTransitionBlockerList($subject, string $transitionName): Tr * * @throws LogicException If the transition is not applicable */ - public function apply($subject, $transitionName); + public function apply($subject, $transitionName, array $context = []); /** * Returns all enabled transitions.