Techblog: Symfony Workflow component (1/3)
With the release of Symfony 3.2 a new workflow component was added that implemented a workflow net or a state machine. This component helps to separate the business workflow logic from other application logic and can help to visualize the workflow and mark the current state it is in.
This blog post is part 1 of a small series on the workflow component:
Figure 1: Creditcard application workflow
- Introduction to the workflow component (this blog post)
- Scheduled transitions using a job scheduler, for example to send reminders.
- Improve the configuration, testability and visualize the workflow
For this introduction we’ll use a creditcard application usecase as example. The applicant will need to confirm the applications emailaddress, the application needs to be verified and a new creditcard needs to be created.
For the workflow component to work we need to configure a workflow definition, a marking store and a subject. A workflow definition consists of places (states), transitions between the places and guards that conditionally block transitions.
The marking store
The marking store keeps track of the place where the subject (in this case the creditcard application) is inside the workflow. By default the marking store will manipulate the ‘marking’ property on your subject. If we have a creditcard application
$application in the place
start and apply the transition
confirm_email the marking store would set the
By default the marking store will manipulate the ‘marking’ property on your subject, but you can change this by supplying an arguments property to the marking_store configuration. In the following example we change the property name to ‘state’.
The marking store will only manipulate the property, but will not take care of any persistence. If we implemented a service
persisting_marking_store with our own implementation of the MarkingStoreInterface we could configure it using the service configuration property.
When the workflow supports parallel processes the subject can be in multiple places at once and we’ll need to configure the MultipleStateMarkingStore.
Figure 2: Multiple-state workflow
But in our example we’ll use a state machine which always has a single place/state.
The type names for marking stores were changed:
– property_accessor -> multiple_state
– scalar -> single_state
You might still see examples with the old configuration names.
When your workflow type is
multiple_state marking store is valid but doesn’t add any extra functionality as a state machine has only a single state.
To get a hold of a specific workflow you can use the following service name syntax:
$container->get('workflow.' . $workflowName), or when you use a state machine:
$container->get('state_machine' . $workflowName). Keep in mind that while the service name differs between a normal workflow and a statemachine the prefix
workflow for event names in both cases.
Apply workflow transition
You can apply a transition using
$workflow->apply('confirm_email', $subject). For our example application we implement a controller that would confirm the emailaddress using a link containing a unique hash.
Applying a transition will dispatch the following sequence of events:
- Guard the transition
The guard event is used the apply conditions to a transition and can block the transition.
- Leave the from place
This event can be used to apply an action on this transition such as sending an email.
- Enter the to place
- Entered the to place (Only ≥ Symfony 3.3)
This is the same as the enter event but now the marking store is updated.
- Announce new applicable transitions
Listener to this event if you want to automatically apply a transition.
Listen for a transition
Using the generated workflow events we could listen for the
confirm_emailtransition event and send an email when the user confirms his creditcard application.
Guard a workflow transition
To conditionally guard a workflow transition the application can listen for guard events. If any of the guard listeners set
$guardEvent->setBlocked(true)the transition will not be available.
In the creditcard example we can require an additional approved credit limit when the user requests a creditcard with a limit above $ 200. We implement a Guard listener that listens for the
In Symfony 3.3+ you will also be able to define the guards using an expression in the workflow definition:
In part 2 of this serie we’ll have a look at how you can schedule transitions and hook transitions to application events. We’ll build a reminder transtion that ‘ll be applied if the user hasn’t confirmed its creditcard application in 3 days.
- https://github.com/symfony/symfony/pull/21935 (Guard expression support)
- https://github.com/symfony/symfony/pull/20787 (Support for the
entered workflow event)