New in Symfony 4.3: Workflow improvements

Warning: This post is about an unsupported Symfony version. Some of this information may be out of date. Read the most recent Symfony Docs.
In Symfony 4.3 we improved the Workflow component with lots of major and minor features. This blog post summarizes the most important ones.
Added a context to Workflow::apply()
Contributed by
Grégoire Pineau
in #29146.
When applying a transition, now it's possible to pass a custom $context
(e.g. the user who performed the transition or the current date):
1 2 3
$workflow->apply($article, $request->request->get('transition'), [
'time' => date('y-m-d H:i:s'),
]);
Before using this feature, update your entity or any other object supported by the workflow as follows:
1 2 3 4
class Article
{
- public function setMarking($marking)
+ public function setMarking($marking, $context = [])
Then, update the configuration of the workflow to use the MethodMarkingStore
:
1 2 3 4 5 6 7
framework:
workflows:
article:
type: workflow
marking_store:
- type: multiple_state
+ type: method
Allow to modify the context in a listener
Contributed by
Grégoire Pineau
in #30902.
Passing the context on every call to ->apply()
could be annoying and will
lead to duplicated code.
Now you can create a listener that does that for you:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
class TransitionEventSubscriber implements EventSubscriberInterface
{
private $tokenStorage;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function onWorkflowArticleTransition(TransitionEvent $event)
{
$context = $event->getContext();
$token = $this->tokenStorage->getToken();
if ($token instanceof TokenInterface) {
$user = $token->getUser();
if ($user instanceof UserInterface) {
$context['user'] = $user->getUsername();
}
}
$event->setContext($context);
}
public static function getSubscribedEvents()
{
return [
TransitionEvent::class => 'onWorkflowArticleTransition',
];
}
}
Added color to dumped workflow
Contributed by
Alexis Lefebvre
in #29538.
It's now possible to configure how a workflow will be rendered thanks to the
dump_style
metadata config option:
1 2 3 4 5 6 7 8 9 10
transitions:
submit:
from: start
to: travis
metadata:
title: transition submit title
dump_style:
label: 'My custom label'
arrow_color: '#0088FF'
label_color: 'Red'
And this is how the custom style would look like:

Allow to configure many initial places
Contributed by
Grégoire Pineau
in #30468
and #30890.
Unlike state machines, when using a workflow it's possible to have a subject in many places. That's why the component now allows to configure multiple initial places:
1 2 3 4 5
workflows:
article:
type: workflow
initial_marking: [foo, bar]
places: [foo, bar, a, b, c, d]
Simpler configuration
Contributed by
Grégoire Pineau
in #30551
and #30890.
As mentioned above, subjects can only be in one place in state machines but they can be in one or more states when using a workflow. However, the initial design of the Workflow component allowed to use a workflow with a Single State Marking Store. This wasn't the best decision and it added some unnecessary complexity.
Starting from Symfony 4.3, if your subject can be only in one state, use a state
machine. In that case, the property (called marking
by default) will be a
string. If the subject can be in many places, use a workflow. In that case, the
property will be an array.
Thanks to this simplification, we've improved the DX (developer experience):
1 2 3 4 5 6 7 8 9 10 11 12
framework:
workflows:
article:
type: workflow
marking_store:
type: method # This will be the default value in Symfony 5.0
property: marking # This is the default value, it could be omitted
task:
type: state_machine
marking_store:
type: method # This will be the default value in Symfony 5.0
property: state
Added workflow_transition_blockers()
Twig function
Contributed by
Grégoire Pineau
in #30908.
In Symfony 4.1 we added a feature to know why a transition is blocked.
In Symfony 4.3 we're adding a Twig function to build the blocker list:
1 2 3 4 5 6 7 8 9 10 11 12
<h2>Publication was blocked because:</h2>
<ul>
{% for blocker in workflow_transition_blockers(article, 'publish') %}
<li>
{{ blocker.message }}
{# Display the guard expression #}
{% if blocker.parameters.expression is defined %}
<code>{{ blocker.parameters.expression }}</code>
{% endif %}
</li>
{% endfor %}
<ul>
Help the Symfony project!
As with any Open-Source project, contributing code or documentation is the most common way to help, but we also have a wide range of sponsoring opportunities.
Comments
Comments are closed.
To ensure that comments stay relevant, they are closed for old posts.
Can't wait to use these improvments :)