Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 199032a

Browse filesBrowse files
committed
[Workflow] Cleaned the transition blocker implementations
1 parent 00530e0 commit 199032a
Copy full SHA for 199032a

9 files changed

+235
-332
lines changed

‎src/Symfony/Component/Workflow/Event/GuardEvent.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Workflow/Event/GuardEvent.php
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,12 @@ public function isBlocked(): bool
4242
public function setBlocked(bool $blocked): void
4343
{
4444
if (!$blocked) {
45-
$this->transitionBlockerList = new TransitionBlockerList();
45+
$this->transitionBlockerList->reset();
4646

4747
return;
4848
}
4949

50-
$this->transitionBlockerList->add(TransitionBlocker::createUnknownReason($this->getTransition()->getName()));
50+
$this->transitionBlockerList->add(TransitionBlocker::createUnknown());
5151
}
5252

5353
public function getTransitionBlockerList(): TransitionBlockerList

‎src/Symfony/Component/Workflow/EventListener/GuardListener.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Workflow/EventListener/GuardListener.php
+6-2Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Symfony\Component\Validator\Validator\ValidatorInterface;
1919
use Symfony\Component\Workflow\Event\GuardEvent;
2020
use Symfony\Component\Workflow\Exception\InvalidTokenConfigurationException;
21+
use Symfony\Component\Workflow\TransitionBlocker;
2122

2223
/**
2324
* @author Grégoire Pineau <lyrixx@lyrixx.info>
@@ -49,8 +50,11 @@ public function onTransition(GuardEvent $event, $eventName)
4950
return;
5051
}
5152

52-
if (!$this->expressionLanguage->evaluate($this->configuration[$eventName], $this->getVariables($event))) {
53-
$event->setBlocked(true);
53+
$expression = $this->configuration[$eventName];
54+
55+
if (!$this->expressionLanguage->evaluate($expression, $this->getVariables($event))) {
56+
$blocker = TransitionBlocker::createBlockedByExpressionGuardListener($expression);
57+
$event->addTransitionBlocker($blocker);
5458
}
5559
}
5660

‎src/Symfony/Component/Workflow/Exception/BlockedTransitionException.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Workflow/Exception/BlockedTransitionException.php
+21-2Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,41 @@
1111

1212
namespace Symfony\Component\Workflow\Exception;
1313

14+
use Symfony\Component\Workflow\Transition;
15+
use Symfony\Component\Workflow\TransitionBlocker;
1416
use Symfony\Component\Workflow\TransitionBlockerList;
1517

1618
/**
1719
* Thrown by the workflow when a transition is not enabled.
20+
*
21+
* @author Grégoire Pineau <lyrixx@lyrixx.info>
1822
*/
1923
class BlockedTransitionException extends LogicException
2024
{
25+
private $transition;
2126
private $transitionBlockerList;
2227

23-
public function __construct(string $message, TransitionBlockerList $transitionBlockerList)
28+
public function __construct(Transition $transition, TransitionBlockerList $transitionBlockerList)
2429
{
25-
parent::__construct($message);
30+
parent::__construct(sprintf('The transition "%s" has been blocked.', $transition->getName()));
2631

32+
$this->transition = $transition;
2733
$this->transitionBlockerList = $transitionBlockerList;
2834
}
2935

36+
public static function createWithBlocker(Transition $transition, TransitionBlocker $transitionBlocker)
37+
{
38+
$transitionBlockerList = new TransitionBlockerList();
39+
$transitionBlockerList->add($transitionBlocker);
40+
41+
return new self($transition, $transitionBlockerList);
42+
}
43+
44+
public function getTransition(): Transition
45+
{
46+
return $this->transition;
47+
}
48+
3049
public function getTransitionBlockerList(): TransitionBlockerList
3150
{
3251
return $this->transitionBlockerList;

‎src/Symfony/Component/Workflow/Exception/UndefinedTransitionException.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Workflow/Exception/UndefinedTransitionException.php
+6Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,13 @@
1313

1414
/**
1515
* Thrown by Workflow when an undefined transition is applied on a subject.
16+
*
17+
* @author Grégoire Pineau <lyrixx@lyrixx.info>
1618
*/
1719
class UndefinedTransitionException extends LogicException
1820
{
21+
public function __construct(string $transitionName, string $workflowName)
22+
{
23+
parent::__construct(sprintf('The transition "%s" is not defined for the workflow "%s".', $transitionName, $workflowName));
24+
}
1925
}

‎src/Symfony/Component/Workflow/Tests/WorkflowTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Workflow/Tests/WorkflowTest.php
+97-124Lines changed: 97 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Symfony\Component\Workflow\Event\Event;
99
use Symfony\Component\Workflow\Event\GuardEvent;
1010
use Symfony\Component\Workflow\Exception\BlockedTransitionException;
11+
use Symfony\Component\Workflow\Exception\UndefinedTransitionException;
1112
use Symfony\Component\Workflow\Marking;
1213
use Symfony\Component\Workflow\MarkingStore\MarkingStoreInterface;
1314
use Symfony\Component\Workflow\MarkingStore\MultipleStateMarkingStore;
@@ -164,20 +165,6 @@ public function testCanDoesNotTriggerGuardEventsForNotEnabledTransitions()
164165
$this->assertSame(array('workflow_name.guard.t3'), $dispatchedEvents);
165166
}
166167

167-
/**
168-
* @expectedException \Symfony\Component\Workflow\Exception\LogicException
169-
* @expectedExceptionMessage Unable to apply transition "t2" for workflow "unnamed".
170-
*/
171-
public function testApplyWithImpossibleTransition()
172-
{
173-
$definition = $this->createComplexWorkflowDefinition();
174-
$subject = new \stdClass();
175-
$subject->marking = null;
176-
$workflow = new Workflow($definition, new MultipleStateMarkingStore());
177-
178-
$workflow->apply($subject, 't2');
179-
}
180-
181168
public function testCanWithSameNameTransition()
182169
{
183170
$definition = $this->createWorkflowWithSameNameTransition();
@@ -195,6 +182,102 @@ public function testCanWithSameNameTransition()
195182
$this->assertTrue($workflow->can($subject, 'to_a'));
196183
}
197184

185+
public function testCanWithThrowReturnsUndefinedTransition()
186+
{
187+
$definition = $this->createSimpleWorkflowDefinition();
188+
$subject = new \stdClass();
189+
$subject->marking = null;
190+
$workflow = new Workflow($definition);
191+
192+
try {
193+
$workflow->can($subject, 'not defined', true);
194+
195+
$this->fail('Workflow failed to throw undefined transition exception.');
196+
} catch (\Exception $e) {
197+
$this->assertInstanceOf(UndefinedTransitionException::class, $e);
198+
}
199+
200+
$this->assertSame('The transition "not defined" is not defined for the workflow "unnamed".', $e->getMessage());
201+
}
202+
203+
public function testCanWithThrowReturnsReasonsProvidedByMarking()
204+
{
205+
$definition = $this->createComplexWorkflowDefinition();
206+
$subject = new \stdClass();
207+
$subject->marking = null;
208+
$workflow = new Workflow($definition, new MultipleStateMarkingStore());
209+
210+
try {
211+
$workflow->can($subject, 't2', true);
212+
213+
$this->fail('Workflow failed to build the blocker list.');
214+
} catch (\Exception $e) {
215+
$this->assertInstanceOf(BlockedTransitionException::class, $e);
216+
}
217+
218+
$this->assertSame('The transition "t2" has been blocked.', $e->getMessage());
219+
$transitionBlockerList = $e->getTransitionBlockerList();
220+
$this->assertCount(1, $transitionBlockerList);
221+
$blockers = iterator_to_array($transitionBlockerList);
222+
$this->assertSame('The marking does not enabled the transition.', $blockers[0]->getMessage());
223+
$this->assertSame('19beefc8-6b1e-4716-9d07-a39bd6d16e34', $blockers[0]->getCode());
224+
}
225+
226+
public function testCanWithThrowReturnsReasonsProvidedInGuards()
227+
{
228+
$definition = $this->createSimpleWorkflowDefinition();
229+
$subject = new \stdClass();
230+
$subject->marking = null;
231+
$dispatcher = new EventDispatcher();
232+
$workflow = new Workflow($definition, new MultipleStateMarkingStore(), $dispatcher);
233+
234+
$dispatcher->addListener('workflow.guard', function (GuardEvent $event) {
235+
$event->addTransitionBlocker(new TransitionBlocker('Transition blocker 1', 'blocker_1'));
236+
$event->addTransitionBlocker(new TransitionBlocker('Transition blocker 2', 'blocker_2'));
237+
});
238+
$dispatcher->addListener('workflow.guard', function (GuardEvent $event) {
239+
$event->addTransitionBlocker(new TransitionBlocker('Transition blocker 3', 'blocker_3'));
240+
});
241+
$dispatcher->addListener('workflow.guard', function (GuardEvent $event) {
242+
$event->setBlocked(true);
243+
});
244+
245+
try {
246+
$workflow->can($subject, 't1', true);
247+
248+
$this->fail('Workflow failed to build the blocker list.');
249+
} catch (\Exception $e) {
250+
$this->assertInstanceOf(BlockedTransitionException::class, $e);
251+
}
252+
253+
$this->assertSame('The transition "t1" has been blocked.', $e->getMessage());
254+
$transitionBlockerList = $e->getTransitionBlockerList();
255+
$this->assertCount(4, $transitionBlockerList);
256+
$blockers = iterator_to_array($transitionBlockerList);
257+
$this->assertSame('Transition blocker 1', $blockers[0]->getMessage());
258+
$this->assertSame('blocker_1', $blockers[0]->getCode());
259+
$this->assertSame('Transition blocker 2', $blockers[1]->getMessage());
260+
$this->assertSame('blocker_2', $blockers[1]->getCode());
261+
$this->assertSame('Transition blocker 3', $blockers[2]->getMessage());
262+
$this->assertSame('blocker_3', $blockers[2]->getCode());
263+
$this->assertSame('Unknown reason.', $blockers[3]->getMessage());
264+
$this->assertSame('e8b5bbb9-5913-4b98-bfa6-65dbd228a82a', $blockers[3]->getCode());
265+
}
266+
267+
/**
268+
* @expectedException \Symfony\Component\Workflow\Exception\LogicException
269+
* @expectedExceptionMessage Unable to apply transition "t2" for workflow "unnamed".
270+
*/
271+
public function testApplyWithImpossibleTransition()
272+
{
273+
$definition = $this->createComplexWorkflowDefinition();
274+
$subject = new \stdClass();
275+
$subject->marking = null;
276+
$workflow = new Workflow($definition, new MultipleStateMarkingStore());
277+
278+
$workflow->apply($subject, 't2');
279+
}
280+
198281
public function testApply()
199282
{
200283
$definition = $this->createComplexWorkflowDefinition();
@@ -413,116 +496,6 @@ public function testGetEnabledTransitionsWithSameNameTransition()
413496
$this->assertSame('to_a', $transitions[1]->getName());
414497
$this->assertSame('to_a', $transitions[2]->getName());
415498
}
416-
417-
public function testWhyCannotReturnsReasonsProvidedInGuards()
418-
{
419-
$definition = $this->createSimpleWorkflowDefinition();
420-
$subject = new \stdClass();
421-
$subject->marking = null;
422-
$dispatcher = new EventDispatcher();
423-
$workflow = new Workflow($definition, new MultipleStateMarkingStore(), $dispatcher);
424-
425-
$guardsAddingTransitionBlockers = array(
426-
function (GuardEvent $event) {
427-
$event->addTransitionBlocker(new TransitionBlocker('Transition blocker 1', 'blocker_1'));
428-
$event->addTransitionBlocker(new TransitionBlocker('Transition blocker 2', 'blocker_2'));
429-
},
430-
function (GuardEvent $event) {
431-
$event->addTransitionBlocker(new TransitionBlocker('Transition blocker 3', 'blocker_3'));
432-
},
433-
);
434-
435-
foreach ($guardsAddingTransitionBlockers as $guard) {
436-
$dispatcher->addListener('workflow.guard', $guard);
437-
}
438-
439-
$transitionBlockerList = $workflow->buildTransitionBlockerList($subject, 't1');
440-
441-
$this->assertCount(3, $transitionBlockerList);
442-
443-
$assertTransitionBlockerPresentByCodeFn = function (string $code) use ($transitionBlockerList) {
444-
$this->assertNotNull(
445-
$transitionBlockerList->findByCode($code),
446-
sprintf('Workflow did not produce transition blocker with code "%s"', $code)
447-
);
448-
};
449-
450-
$assertTransitionBlockerPresentByCodeFn('blocker_1');
451-
$assertTransitionBlockerPresentByCodeFn('blocker_2');
452-
$assertTransitionBlockerPresentByCodeFn('blocker_3');
453-
}
454-
455-
public function testWhyCannotReturnsTransitionNotDefinedReason()
456-
{
457-
$definition = $this->createSimpleWorkflowDefinition();
458-
$subject = new \stdClass();
459-
$subject->marking = null;
460-
$workflow = new Workflow($definition);
461-
462-
$transitionBlockerList = $workflow->buildTransitionBlockerList($subject, 'undefined_transition_name');
463-
464-
$this->assertCount(1, $transitionBlockerList);
465-
$this->assertEquals(
466-
TransitionBlocker::REASON_TRANSITION_NOT_DEFINED,
467-
$transitionBlockerList->get(0)->getCode()
468-
);
469-
}
470-
471-
public function testWhyCannotReturnsTransitionNotApplicableReason()
472-
{
473-
$definition = $this->createSimpleWorkflowDefinition();
474-
$subject = new \stdClass();
475-
$subject->marking = null;
476-
$workflow = new Workflow($definition);
477-
478-
$transitionBlockerList = $workflow->buildTransitionBlockerList($subject, 't2');
479-
480-
$this->assertCount(1, $transitionBlockerList);
481-
$this->assertEquals(
482-
TransitionBlocker::REASON_TRANSITION_NOT_APPLICABLE,
483-
$transitionBlockerList->get(0)->getCode()
484-
);
485-
}
486-
487-
public function testApplyConveysTheTransitionBlockers()
488-
{
489-
$definition = $this->createSimpleWorkflowDefinition();
490-
$subject = new \stdClass();
491-
$subject->marking = null;
492-
$dispatcher = new EventDispatcher();
493-
$workflow = new Workflow($definition, new MultipleStateMarkingStore(), $dispatcher);
494-
495-
$dispatcher->addListener('workflow.guard', function (GuardEvent $event) {
496-
$event->addTransitionBlocker(new TransitionBlocker('Transition blocker 3', 'blocker_1'));
497-
});
498-
499-
try {
500-
$workflow->apply($subject, 't1');
501-
} catch (BlockedTransitionException $exception) {
502-
$this->assertNotNull(
503-
$exception->getTransitionBlockerList()->findByCode('blocker_1'),
504-
'Workflow failed to convey it could not transition subject because of expected blocker'
505-
);
506-
507-
return;
508-
}
509-
510-
$this->fail('Workflow failed to prevent a transition from happening');
511-
}
512-
513-
/**
514-
* @expectedException \Symfony\Component\Workflow\Exception\UndefinedTransitionException
515-
* @expectedExceptionMessage Transition "undefined_transition" is not defined in workflow "unnamed".
516-
*/
517-
public function testApplyWithUndefinedTransition()
518-
{
519-
$definition = $this->createSimpleWorkflowDefinition();
520-
$subject = new \stdClass();
521-
$subject->marking = null;
522-
$workflow = new Workflow($definition);
523-
524-
$workflow->apply($subject, 'undefined_transition');
525-
}
526499
}
527500

528501
class EventDispatcherMock implements \Symfony\Component\EventDispatcher\EventDispatcherInterface

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.