Grégoire Pineau Fabien Potencier
Contributed by Grégoire Pineau and Fabien Potencier in #11882

Workflows are a fundamental element in lots of organizations' structures. They describe a sequence of operations that can be executed repeatedly to provide some service (e.g. buying a product in an e-commerce application), process some information (e.g. publishing some content in a CMS application), etc.

In Symfony 3.2 we added a new Workflow component to help you define those workflows in your applications. Technically, the component implements a "workflow net", which is a subclass of the Petri net.

In practice, to create a workflow you define "states" and "transitions" (which are the events that may occur between two states). The following example shows a minimal workflow to publish some content:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
framework:
    workflows:
        article_publishing:
            marking_store:
                type: property_accessor
            supports:
                - AppBundle\Entity\Article
            places:
                - draft
                - spellchecked
                - published
            transitions:
                spellcheck:
                    from: draft
                    to:   spellchecked
                publish:
                    from: spellchecked
                    to:   published

Now you can start using this workflow in your templates and controllers. For example, in a template:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{# the workflow name is optional when there is just one workflow for the class #}
{% if workflow_can(article, 'publish') %}
    <a href="...">Publish article</a>
{% endif %}

{# if more than one workflow is defined for the 'Article' class #}
{% if workflow_can(article, 'publish', 'article_publishing') %}
    <a href="...">Publish article</a>
{% endif %}

{# ... #}

{% for transition in workflow_transitions(article) %}
    <a href="...">{{ transition.name }}</a>
{% else %}
    No actions available for this article.
{% endfor %}

In a controller, you can get any defined workflow by its name thanks to the workflow registry created by Symfony and then, apply any given transition to it:

1
2
3
4
5
6
7
8
9
10
11
use Symfony\Component\Workflow\Exception\LogicException;

public function reviewAction(Article $article)
{
    // the try/catch is needed because this transition could already have been applied
    try {
        $this->get('workflow.article_publishing')->apply($article, 'spellcheck');
    } catch (LogicException $e) {
        // ...
    }
}

If you want to execute custom logic when a transition happens, you can hook listeners to the events triggered by the component.

Check out this demo application for a full example of the workflow component in action and check out this GitHub project for an unofficial port of the component for Symfony 2.3+ versions.

Published in #Living on the edge