Skip to content

Commit

Permalink
feature #29146 [Workflow] Added a context to Workflow::apply() (lyr…
Browse files Browse the repository at this point in the history
…ixx)

This PR was merged into the 4.3-dev branch.

Discussion
----------

[Workflow] Added a context to `Workflow::apply()`

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #27925 (maybe #28253 and #28266)
| License       | MIT
| Doc PR        |

Commits
-------

7d5b7a3 [Workflow] Added a context to `Workflow::apply()`
  • Loading branch information
lyrixx committed Mar 6, 2019
2 parents f8664e7 + 7d5b7a3 commit 5b38e17
Show file tree
Hide file tree
Showing 13 changed files with 176 additions and 13 deletions.
Expand Up @@ -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()
Expand Down
Expand Up @@ -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
Expand Down
Expand Up @@ -22,6 +22,7 @@

<service id="workflow.marking_store.multiple_state" class="Symfony\Component\Workflow\MarkingStore\MultipleStateMarkingStore" abstract="true" />
<service id="workflow.marking_store.single_state" class="Symfony\Component\Workflow\MarkingStore\SingleStateMarkingStore" abstract="true" />
<service id="workflow.marking_store.method" class="Symfony\Component\Workflow\MarkingStore\MethodMarkingStore" abstract="true" />

<service id="workflow.registry" class="Symfony\Component\Workflow\Registry" />
<service id="Symfony\Component\Workflow\Registry" alias="workflow.registry" />
Expand Down
Expand Up @@ -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));

Expand All @@ -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));

Expand Down
1 change: 1 addition & 0 deletions src/Symfony/Component/Workflow/CHANGELOG.md
Expand Up @@ -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
-----
Expand Down
Expand Up @@ -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);
}
}
Expand Up @@ -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 = []);
}
83 changes: 83 additions & 0 deletions src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php
@@ -0,0 +1,83 @@
<?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\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 <lyrixx@lyrixx.info>
*/
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);
}
}
Expand Up @@ -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());
}
Expand Down
Expand Up @@ -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()));
}
Expand Down
@@ -0,0 +1,74 @@
<?php

namespace Symfony\Component\Workflow\Tests\MarkingStore;

use PHPUnit\Framework\TestCase;
use Symfony\Component\Workflow\Marking;
use Symfony\Component\Workflow\MarkingStore\MethodMarkingStore;

class MethodMarkingStoreTest extends TestCase
{
public function testGetSetMarkingWithMultipleState()
{
$subject = new Subject();

$markingStore = new MethodMarkingStore(false);

$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' => 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;
}
}
4 changes: 2 additions & 2 deletions src/Symfony/Component/Workflow/Workflow.php
Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Component/Workflow/WorkflowInterface.php
Expand Up @@ -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.
Expand Down

0 comments on commit 5b38e17

Please sign in to comment.