Added Context to Workflow Event

There's also a default context for the initial marking event.
This commit is contained in:
Carlos Pereira De Amorim 2020-07-10 00:08:44 +02:00
parent 812a4d5a27
commit bfe07dda83
6 changed files with 95 additions and 28 deletions

View File

@ -21,6 +21,7 @@ use Twig\TwigFunction;
* WorkflowExtension.
*
* @author Grégoire Pineau <lyrixx@lyrixx.info>
* @author Carlos Pereira De Amorim <carlos@shauri.fr>
*/
final class WorkflowExtension extends AbstractExtension
{

View File

@ -5,6 +5,8 @@ CHANGELOG
-----
* Added `Workflow::getEnabledTransition()` to easily retrieve a specific transition object
* Added context to the event dispatched
* Added default context to the Initial Marking
5.1.0
-----

View File

@ -19,20 +19,23 @@ use Symfony\Contracts\EventDispatcher\Event as BaseEvent;
/**
* @author Fabien Potencier <fabien@symfony.com>
* @author Grégoire Pineau <lyrixx@lyrixx.info>
* @author Carlos Pereira De Amorim <carlos@shauri.fr>
*/
class Event extends BaseEvent
{
protected $context;
private $subject;
private $marking;
private $transition;
private $workflow;
public function __construct(object $subject, Marking $marking, Transition $transition = null, WorkflowInterface $workflow = null)
public function __construct(object $subject, Marking $marking, Transition $transition = null, WorkflowInterface $workflow = null, array $context = [])
{
$this->subject = $subject;
$this->marking = $marking;
$this->transition = $transition;
$this->workflow = $workflow;
$this->context = $context;
}
public function getMarking()
@ -64,4 +67,9 @@ class Event extends BaseEvent
{
return $this->workflow->getMetadataStore()->getMetadata($key, $subject);
}
public function getContext(): array
{
return $this->context;
}
}

View File

@ -13,15 +13,8 @@ namespace Symfony\Component\Workflow\Event;
final class TransitionEvent extends Event
{
private $context;
public function setContext(array $context): void
{
$this->context = $context;
}
public function getContext(): array
{
return $this->context;
}
}

View File

@ -392,6 +392,7 @@ class WorkflowTest extends TestCase
$eventNameExpected = [
'workflow.entered',
'workflow.workflow_name.entered',
'workflow.workflow_name.entered.a',
'workflow.guard',
'workflow.workflow_name.guard',
'workflow.workflow_name.guard.t1',
@ -463,6 +464,7 @@ class WorkflowTest extends TestCase
$eventNameExpected = [
'workflow.entered',
'workflow.workflow_name.entered',
'workflow.workflow_name.entered.a',
'workflow.guard',
'workflow.workflow_name.guard',
'workflow.workflow_name.guard.a-b',
@ -533,6 +535,58 @@ class WorkflowTest extends TestCase
$workflow->apply($subject, 't1');
}
public function testEventContext()
{
$definition = $this->createComplexWorkflowDefinition();
$subject = new Subject();
$dispatcher = new EventDispatcher();
$name = 'workflow_name';
$context = ['context'];
$workflow = new Workflow($definition, new MethodMarkingStore(), $dispatcher, $name);
$assertWorkflowContext = function (Event $event) use ($context) {
$this->assertEquals($context, $event->getContext());
};
$eventNames = [
'workflow.leave',
'workflow.transition',
'workflow.enter',
'workflow.entered',
'workflow.announce',
];
foreach ($eventNames as $eventName) {
$dispatcher->addListener($eventName, $assertWorkflowContext);
}
$workflow->apply($subject, 't1', $context);
}
public function testEventDefaultInitialContext()
{
$definition = $this->createComplexWorkflowDefinition();
$subject = new Subject();
$dispatcher = new EventDispatcher();
$name = 'workflow_name';
$context = Workflow::DEFAULT_INITIAL_CONTEXT;
$workflow = new Workflow($definition, new MethodMarkingStore(), $dispatcher, $name);
$assertWorkflowContext = function (Event $event) use ($context) {
$this->assertEquals($context, $event->getContext());
};
$eventNames = [
'workflow.workflow_name.entered.a',
];
foreach ($eventNames as $eventName) {
$dispatcher->addListener($eventName, $assertWorkflowContext);
}
$workflow->apply($subject, 't1');
}
public function testMarkingStateOnApplyWithEventDispatcher()
{
$definition = new Definition(range('a', 'f'), [new Transition('t', range('a', 'c'), range('d', 'f'))]);

View File

@ -30,10 +30,12 @@ use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
* @author Fabien Potencier <fabien@symfony.com>
* @author Grégoire Pineau <lyrixx@lyrixx.info>
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
* @author Carlos Pereira De Amorim <carlos@shauri.fr>
*/
class Workflow implements WorkflowInterface
{
public const DISABLE_ANNOUNCE_EVENT = 'workflow_disable_announce_event';
public const DEFAULT_INITIAL_CONTEXT = ['initial' => true];
private $definition;
private $markingStore;
@ -51,7 +53,7 @@ class Workflow implements WorkflowInterface
/**
* {@inheritdoc}
*/
public function getMarking(object $subject)
public function getMarking(object $subject, array $context = [])
{
$marking = $this->markingStore->getMarking($subject);
@ -71,7 +73,11 @@ class Workflow implements WorkflowInterface
// update the subject with the new marking
$this->markingStore->setMarking($subject, $marking);
$this->entered($subject, null, $marking);
if (!$context) {
$context = self::DEFAULT_INITIAL_CONTEXT;
}
$this->entered($subject, null, $marking, $context);
}
// check that the subject has a known place
@ -154,7 +160,7 @@ class Workflow implements WorkflowInterface
*/
public function apply(object $subject, string $transitionName, array $context = [])
{
$marking = $this->getMarking($subject);
$marking = $this->getMarking($subject, $context);
$transitionExist = false;
$approvedTransitions = [];
@ -197,20 +203,20 @@ class Workflow implements WorkflowInterface
}
foreach ($approvedTransitions as $transition) {
$this->leave($subject, $transition, $marking);
$this->leave($subject, $transition, $marking, $context);
$context = $this->transition($subject, $transition, $marking, $context);
$this->enter($subject, $transition, $marking);
$this->enter($subject, $transition, $marking, $context);
$this->markingStore->setMarking($subject, $marking, $context);
$this->entered($subject, $transition, $marking);
$this->entered($subject, $transition, $marking, $context);
$this->completed($subject, $transition, $marking);
$this->completed($subject, $transition, $marking, $context);
if (!($context[self::DISABLE_ANNOUNCE_EVENT] ?? false)) {
$this->announce($subject, $transition, $marking);
$this->announce($subject, $transition, $marking, $context);
}
}
@ -324,12 +330,12 @@ class Workflow implements WorkflowInterface
return $event;
}
private function leave(object $subject, Transition $transition, Marking $marking): void
private function leave(object $subject, Transition $transition, Marking $marking, array $context = []): void
{
$places = $transition->getFroms();
if (null !== $this->dispatcher) {
$event = new LeaveEvent($subject, $marking, $transition, $this);
$event = new LeaveEvent($subject, $marking, $transition, $this, $context);
$this->dispatcher->dispatch($event, WorkflowEvents::LEAVE);
$this->dispatcher->dispatch($event, sprintf('workflow.%s.leave', $this->name));
@ -350,8 +356,7 @@ class Workflow implements WorkflowInterface
return $context;
}
$event = new TransitionEvent($subject, $marking, $transition, $this);
$event->setContext($context);
$event = new TransitionEvent($subject, $marking, $transition, $this, $context);
$this->dispatcher->dispatch($event, WorkflowEvents::TRANSITION);
$this->dispatcher->dispatch($event, sprintf('workflow.%s.transition', $this->name));
@ -360,12 +365,12 @@ class Workflow implements WorkflowInterface
return $event->getContext();
}
private function enter(object $subject, Transition $transition, Marking $marking): void
private function enter(object $subject, Transition $transition, Marking $marking, array $context): void
{
$places = $transition->getTos();
if (null !== $this->dispatcher) {
$event = new EnterEvent($subject, $marking, $transition, $this);
$event = new EnterEvent($subject, $marking, $transition, $this, $context);
$this->dispatcher->dispatch($event, WorkflowEvents::ENTER);
$this->dispatcher->dispatch($event, sprintf('workflow.%s.enter', $this->name));
@ -380,13 +385,13 @@ class Workflow implements WorkflowInterface
}
}
private function entered(object $subject, ?Transition $transition, Marking $marking): void
private function entered(object $subject, ?Transition $transition, Marking $marking, array $context): void
{
if (null === $this->dispatcher) {
return;
}
$event = new EnteredEvent($subject, $marking, $transition, $this);
$event = new EnteredEvent($subject, $marking, $transition, $this, $context);
$this->dispatcher->dispatch($event, WorkflowEvents::ENTERED);
$this->dispatcher->dispatch($event, sprintf('workflow.%s.entered', $this->name));
@ -395,29 +400,33 @@ class Workflow implements WorkflowInterface
foreach ($transition->getTos() as $place) {
$this->dispatcher->dispatch($event, sprintf('workflow.%s.entered.%s', $this->name, $place));
}
} elseif (!empty($this->definition->getInitialPlaces())) {
foreach ($this->definition->getInitialPlaces() as $place) {
$this->dispatcher->dispatch($event, sprintf('workflow.%s.entered.%s', $this->name, $place));
}
}
}
private function completed(object $subject, Transition $transition, Marking $marking): void
private function completed(object $subject, Transition $transition, Marking $marking, array $context): void
{
if (null === $this->dispatcher) {
return;
}
$event = new CompletedEvent($subject, $marking, $transition, $this);
$event = new CompletedEvent($subject, $marking, $transition, $this, $context);
$this->dispatcher->dispatch($event, WorkflowEvents::COMPLETED);
$this->dispatcher->dispatch($event, sprintf('workflow.%s.completed', $this->name));
$this->dispatcher->dispatch($event, sprintf('workflow.%s.completed.%s', $this->name, $transition->getName()));
}
private function announce(object $subject, Transition $initialTransition, Marking $marking): void
private function announce(object $subject, Transition $initialTransition, Marking $marking, array $context): void
{
if (null === $this->dispatcher) {
return;
}
$event = new AnnounceEvent($subject, $marking, $initialTransition, $this);
$event = new AnnounceEvent($subject, $marking, $initialTransition, $this, $context);
$this->dispatcher->dispatch($event, WorkflowEvents::ANNOUNCE);
$this->dispatcher->dispatch($event, sprintf('workflow.%s.announce', $this->name));