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:
- 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 $application->marking
property to email_confirmed
.
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.
For an example of a persisting marking store see the OrmPersistentMarkingStore in the GTT workflow-extension-bundle.
Multiple-state workflow
When the workflow supports parallel processes the subject can be in multiple places at once and we’ll need to configure the MultipleStateMarkingStore.
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 state_machine
a multiple_state
marking store is valid but doesn’t add any extra functionality as a state machine has only a single state.
Initiate workflow
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
Transition events
Applying a transition will dispatch the following sequence of events:
- Guard the transition
workflow[.<workflow_name>].guard[.<transition_name>]
The guard event is used the apply conditions to a transition and can block the transition. - Leave the from place
workflow[.<workflow_name>].leave[.<from_place_name>]
- Transition
workflow[.<workflow_name>].transition[.<transition_name>]
This event can be used to apply an action on this transition such as sending an email. - Enter the to place
workflow[.<workflow_name>].enter[.<to_place_name>]
- Entered the to place (Only ≥ Symfony 3.3)
workflow[.<workflow_name>].entered[.<to_place_name>]
This is the same as the enter event but now the marking store is updated. - Announce new applicable transitions
workflow[.<workflow_name>].transition[.<transition_name>]
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_email
transition 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 workflow.creditcard_application_flow.guard.create_card
event:
In Symfony 3.3+ you will also be able to define the guards using an expression in the workflow definition:
What’s next?
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.
References
- https://github.com/lyrixx/SFLive-Paris2016-Workflow
- http://symfony.com/blog/new-in-symfony-3-2-workflow-component
- https://github.com/symfony/symfony/pull/21935 (Guard expression support)
- https://github.com/symfony/symfony/pull/20787 (Support for the
entered
workflow event) - https://github.com/GlobalTradingTechnologies/workflow-extensions-bundle