Workflow Component Introduction & Demo
In this video we are going to take a look at the Workflow Component, an awesome new feature added into the Symfony framework from Symfony 3.2 onwards. Before we get started, I would like to say a huge thank you to Grégoire Pineau, and Fabien Potencier, along with all other contributors, reviewers, and testers, who played their parts in adding this wonderful component. Thank you.
Before we go any further, I imagine you might wish to know why you would want, or need to use the Workflow Component in your project. Good question.
A workflow allows you to explicitly define the process or lifecycle that your objects go through as they pass through your system.
By creating a workflow definition, you can define all the "legal moves" that your objects can start from, and go to, when they are in a given state. The proper term for these "legal moves" is Transitions.
Essentially the workflow component helps to standardise the next available step, or steps, that you objects can take.
As ever, this is much easier to see in action.
As this component is useful not just moving forwards, there has also been a bundle created to integrate the Workflow Component with Symfony 2.3+. I have no direct experience with this approach.
As part of the reveal of the Workflow Component at Symfony Live Paris 2016, Grégoire Pineau created a fantastic example application to demo the key features offered in this component.
I would strongly recommend you pull this code to your local machine, and review the various files as we go through this video. If you do pull the code, be aware that it comes expecting to connect to a Postgres DB. If you would prefer to use MySQL, that is also possible but you will need to make some changes to the config:
# /app/config/parameters.yml
# example settings, plug in your own DB server values
parameters:
database_host: 192.168.0.21
database_port: null
database_name: sf_paris_workflow_demo
database_user: dbuser
database_password: dbpassword
And to config.yml
:
# /app/config/config.yml
# Doctrine Configuration
doctrine:
dbal:
driver: pdo_mysql
host: "%database_host%"
port: "%database_port%"
dbname: "%database_name%"
user: "%database_user%"
password: "%database_password%"
charset: UTF8
Or, you could choose to browse the code via Github, and use the online demo application.
Rather than try to address all the terminology and configuration up front, we will cover each piece as it becomes directly applicable to what we are trying to do.
A Short Note on the Workflow Terminology
The Workflow Component is based on the 'Workflow net', which is a subclass of a Petri Net. The official Symfony documentation links to the Wikipedia entry for Petri nets, which has a section on the Formal Definitions, and Basic Terminology.
As I am not the most mathematically inclined, this page was both scary and confusing. I will therefore give my layman's interpretation of the terms as we come across them, but do feel free to shout up and correct me if I am wrong.
Starting With State Machines
There are two types of workflows available to us:
- Workflows;
- and State Machines
This is much easier to see visually by way of an example in Symfony configuration format:
framework:
workflows:
your_state_machine_name_here:
type: state_machine
# and / or
your_workflow_name_here:
type: workflow
By default, the type: workflow
is assumed, so if you do not see an explicitly defined type
, you can infer you are working with a type
of workflow
.
The key point to note is that a state_machine
may only be in one place
at once.
A workflow
may be in multiple places concurrently.
The way I think about a place
is as another name for state
.
Again, better illustrated with a chunk of configuration:
framework:
workflows:
simple_state_machine_example:
type: state_machine
places:
- a
- b
- c
If you have a workflow with the type
of state_machine
, you can only ever be in one of the three available places
at any one time.
If you have a workflow with the type
of workflow
, you can be in more than one place at any given time.
This makes less sense in the abstract of a
, b
, and c
, but when using a more 'real world' example, as covered in the video, of backlog
, in_progress
, and done
, it does make more sense. You would never want to be both in_progress
, and done
. That would be terribly confusing, but is the sort of thing that seems to inevitably happen in home-rolled, custom solutions once real users get involved.
framework:
workflows:
task:
type: state_machine
places:
- backlog
- in_progress
- done
Each of these individually named workflows - such as task
above, and earlier simple_state_machine_example
, and your_workflow_name_here
, etc, are called "workflow definitions".
Transitioning Between Places
One of the nicest features of the Workflow Component is in its ability to output / dump individual workflow definitions to a standardised format, which can be converted into a pretty visual representation of this definition:

At this point I want to point out that the image you see above is different to that used in the video. I will address this point at the end of this write-up. For now, just be aware that this is the same process.
What we can see here are our three places
, along with the two transitions
(the "legal moves") that define how an object can progress (transition
) from one state (place
) to the next.
Defined in configuration, this looks like:
framework:
workflows:
task:
type: state_machine
supports:
- AppBundle\Entity\Task
places:
- backlog
- in_progress
- done
transitions:
processing:
from: backlog
to: in_progress
done:
from: in_progress
to: done
Note the two new additions here.
Firstly we need to define which object / entity this workflow definition supports
. Note that this is a list, and multiple objects / entities can be used in the same workflow definition.
Next, we have the transitions
key. This is fairly self explanitory. These are the "legal moves" allowed, as defined in - to an extent - the sort of English that a less technically minded person might comprehend. We call these people Project Managers. Ha.
It's important to understand that there is no magic going on here. To transition
between places
, we must make a note of the current place
that a given entity / object is at currently. We do this by marking
the object / entity.
Hand Your Work In For Marking
The concept of Marking again comes from the Petri Net terminology.
Personally, this is my least favourite term in the Workflow Component. And thankfully we can change this with a little extra config, but for the moment, let's go with it, as it's official.
To keep note (or mark
) where our object is in its journey through our workflow definition, the Workflow Component will need to mark
(via the MarkingStore
) the object with its current place
.
By default the Workflow Component will expect a defined getter
and setter
of getMarking
, and setMarking
.
<?php
// /src/AppBundle/Entity/Task.php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Table()
* @ORM\Entity()
*/
class Task
{
/**
* @ORM\Column(type="integer")
* @ORM\Id()
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/** @ORM\Column(type="string", nullable=true) */
private $marking;
public function getMarking()
{
return $this->marking;
}
public function setMarking($marking)
{
$this->marking = $marking;
}
}
Note that as our Task
is part of a state_machine
workflow definition, that the value of $marking
is stored as a string
. If we could be in multiple places at once, this would need to be an array of some kind (e.g. a JSON array).
The state_machine
workflow type is in itself, incredibly useful.
However, as your business requirements inevitably grow, the extra flexibility provided by the type
of workflow
will become potentially the more frequently used of the two.
A Workflow Type of workflow
As mentioned earlier, workflow
is considered the default workflow type
.
And again, the main difference between workflow
and state_machine
is that a workflow
may be in more than one place at any given time.

Note here that after we make the transition
from draft
, we are in two places
concurrently:
wait_for_journalist
wait_for_spellchecker
It was around this point that I started to get quite excited about the possibilities that the Workflow Component would open up for me in my own projects. I hope you feel the same way.
framework:
workflows:
article:
type: workflow
supports:
- AppBundle\Entity\Article
places:
- draft
- wait_for_journalist
- wait_for_spellchecker
transitions:
request_review:
from: draft
to:
- wait_for_journalist
- wait_for_spellchecker
I have removed all the extra parts of this workflow definition in order to focus only on the parts relevant to our introduction.
We know of the type
, and supports
.
We know that we can define places
.
We've also covered transitions
, and how a transition
has a from
and a to
.
The really cool part is that a from
, and a to
, can both take a list of places
. In doing so, as soon as our entity / object transitions through request_review
, it will be marked with both places
.
However, in order to do so we would need an entity with a marking
property capable of storing arrays:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Table()
* @ORM\Entity()
*/
class Article
{
/**
* @ORM\Column(type="integer")
* @ORM\Id()
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/** @ORM\Column(type="json_array", nullable=true) */
private $marking;
public function getId()
{
return $this->id;
}
public function getMarking()
{
return $this->marking;
}
public function setMarking($marking)
{
$this->marking = $marking;
}
}
And with that we have covered a whole bunch of things from a very high level.
Whilst this demo code is fantastic for seeing how the Workflow Component can be used, personally I found it a little overwhelming to use a base for my learning. It does, however, make an absolutely fantastic reference guide.
With this in mind, in the very next video we will start by implementing our own state_machine
workflow definition, diving a little deeper into where the options are coming from, what options are available, how to override the marking
configuration, and so on.