Skip to content

Commit ba49091

Browse files
Nyholmxabbuh
authored andcommitted
Added docs for Workflow component
1 parent 7a8e74a commit ba49091

11 files changed

+613
-3
lines changed
25.9 KB
Loading
Loading
63.6 KB
Loading
8.93 KB
Loading
Loading

components/workflow.rst

+84-3
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
The Workflow Component
66
======================
77

8-
The Workflow component provides tools for managing a workflow or finite state
9-
machine.
8+
The Workflow component provides tools for managing a workflow or finite
9+
state machine.
1010

1111
.. versionadded:: 3.2
1212
The Workflow component was introduced in Symfony 3.2.
@@ -19,6 +19,87 @@ You can install the component in 2 different ways:
1919
* :doc:`Install it via Composer </components/using_components>` (``symfony/workflow`` on `Packagist`_);
2020
* Use the official Git repository (https://github.com/symfony/workflow).
2121

22-
For more information, see the code in the Git Repository.
22+
.. include:: /components/require_autoload.rst.inc
23+
24+
Creating a Workflow
25+
-------------------
26+
27+
The workflow component gives you an object oriented way to define a process
28+
or a life cycle that your object goes through. Each step or stage in the
29+
process is called a *place*. You do also define *transitions* that describe
30+
the action to get from one place to another.
31+
32+
.. image:: /_images/components/workflow/states_transitions.png
33+
34+
A set of places and transitions creates a **definition**. A workflow needs
35+
a ``Definition`` and a way to write the states to the objects (i.e. an
36+
instance of a :class:`Symfony\\Component\\Workflow\\MarkingStore\\MarkingStoreInterface`).
37+
38+
Consider the following example for a blog post. A post can have one of a number
39+
of predefined statuses (`draft`, `review`, `rejected`, `published`). In a workflow,
40+
these statuses are called **places**. You can define the workflow like this::
41+
42+
use Symfony\Component\Workflow\DefinitionBuilder;
43+
use Symfony\Component\Workflow\Transition;
44+
use Symfony\Component\Workflow\Workflow;
45+
use Symfony\Component\Workflow\MarkingStore\SingleStateMarkingStore;
46+
47+
$builder = new DefinitionBuilder();
48+
$builder->addPlaces(['draft', 'review', 'rejected', 'published']);
49+
50+
// Transitions are defined with a unique name, an origin place and a destination place
51+
$builder->addTransition(new Transition('to_review', 'draft', 'review'));
52+
$builder->addTransition(new Transition('publish', 'review', 'published'));
53+
$builder->addTransition(new Transition('reject', 'review', 'rejected'));
54+
55+
$definition = $builder->build();
56+
57+
$marking = new SingleStateMarkingStore('currentState');
58+
$workflow = new Workflow($definition, $marking);
59+
60+
The ``Workflow`` can now help you to decide what actions are allowed
61+
on a blog post depending on what *place* it is in. This will keep your domain
62+
logic in one place and not spread all over your application.
63+
64+
When you define multiple workflows you should consider using a ``Registry``,
65+
which is an object that stores and provides access to different workflows.
66+
A registry will also help you to decide if a workflow supports the object you
67+
are trying to use it with::
68+
69+
use Symfony\Component\Workflow\Registry;
70+
use Acme\Entity\BlogPost;
71+
use Acme\Entity\Newsletter;
72+
73+
$blogWorkflow = ...
74+
$newsletterWorkflow = ...
75+
76+
$registry = new Registry();
77+
$registry->add($blogWorkflow, BlogPost::class);
78+
$registry->add($newsletterWorkflow, Newsletter::class);
79+
80+
Usage
81+
-----
82+
83+
When you have configured a ``Registry`` with your workflows, you may use it as follows::
84+
85+
// ...
86+
$post = new BlogPost();
87+
$workflow = $registry->get($post);
88+
89+
$workflow->can($post, 'publish'); // False
90+
$workflow->can($post, 'to_review'); // True
91+
92+
$workflow->apply($post, 'to_review');
93+
$workflow->can($post, 'publish'); // True
94+
$workflow->getEnabledTransitions($post); // ['publish', 'reject']
95+
96+
Learn more
97+
----------
98+
99+
.. toctree::
100+
:maxdepth: 1
101+
:glob:
102+
103+
/workflow/*
23104

24105
.. _Packagist: https://packagist.org/packages/symfony/workflow

index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ Topics
6262
testing
6363
translation
6464
validation
65+
workflow
6566

6667
Best Practices
6768
--------------

workflow.rst

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
Workflow
2+
========
3+
4+
A workflow is a model of a process in your application. It may be the process
5+
of how a blog post goes from draft, review and publish. Another example is when
6+
a user submits a series of different forms to complete a task. Such processes are
7+
best kept away from your models and should be defined in configuration.
8+
9+
A **definition** of a workflow consist of places and actions to get from one
10+
place to another. The actions are called **transistions**. A workflow does also
11+
need to know each object's position in the workflow. That **marking store** writes
12+
to a property of the object to remember the current place.
13+
14+
.. note::
15+
16+
The terminology above is commonly used when discussing workflows and
17+
`Petri nets`_
18+
19+
The Workflow component does also support state machines. A state machine is a subset
20+
of a workflow and its purpose is to hold a state of your model. Read more about the
21+
differences and specific features of state machine in :doc:`/workflow/state-machines`.
22+
23+
Examples
24+
--------
25+
26+
The simples workflow looks like this. It contains two places and one transition.
27+
28+
.. image:: /_images/components/workflow/simple.png
29+
30+
Workflows could be more complicated when they describe a real business case. The
31+
workflow below describes the process to fill in a job application.
32+
33+
.. image:: /_images/components/workflow/job_application.png
34+
35+
When you fill in a job application in this example there are 4 to 7 steps depending
36+
on the what job you are applying for. Some jobs require personality tests, logic tests
37+
and/or formal requirements to be answered by the user. Some jobs don't. The
38+
``GuardEvent`` is used to decide what next steps are allowed for a specific application.
39+
40+
By defining a workflow like this, there is an overview how the process looks like. The process
41+
logic is not mixed with the controllers, models or view. The order of the steps can be changed
42+
by changing the configuration only.
43+
44+
.. toctree::
45+
:maxdepth: 1
46+
:glob:
47+
48+
workflow/*
49+
50+
.. _Petri nets: https://en.wikipedia.org/wiki/Petri_net

workflow/dumping-workflows.rst

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
.. index::
2+
single: Workflow; Dumping Workflows
3+
4+
How to Dump Workflows
5+
=====================
6+
7+
To help you debug your workflows, you can dump a representation of your workflow with
8+
the use of a ``DumperInterface``. Use the ``GraphvizDumper`` to create a
9+
PNG image of the workflow defined above::
10+
11+
// dump-graph.php
12+
$dumper = new GraphvizDumper();
13+
echo $dumper->dump($definition);
14+
15+
.. code-block:: terminal
16+
17+
$ php dump-graph.php > out.dot
18+
$ dot -Tpng out.dot -o graph.png
19+
20+
The result will look like this:
21+
22+
.. image:: /_images/components/workflow/blogpost.png
23+
24+
If you have configured your workflow with the Symfony framework, you may dump the dot file
25+
with the ``WorkflowDumpCommand``:
26+
27+
.. code-block:: terminal
28+
29+
$ php bin/console workflow:dump name > out.dot
30+
$ dot -Tpng out.dot -o graph.png
31+
32+
.. note::
33+
34+
The ``dot`` command is part of Graphviz. You can download it and read
35+
more about it on `Graphviz.org`_.
36+
37+
.. _Graphviz.org: http://www.graphviz.org

workflow/state-machines.rst

+197
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
.. index::
2+
single: Workflow; Workflows as State Machines
3+
4+
Workflows as State Machines
5+
===========================
6+
7+
The workflow component is modelled after a *Workflow net* which is a subclass
8+
of a `Petri net`_. By adding further restrictions you can get a state machine.
9+
The most important one being that a state machine cannot be in more than
10+
one place simultaneously. It is also worth noting that a workflow does not
11+
commonly have cyclic path in the definition graph, but it is common for a state
12+
machine.
13+
14+
Example of a State Machine
15+
--------------------------
16+
17+
A pull request starts in an intial "start" state, a state for e.g. running
18+
tests on Travis. When this is finished, the pull request is in the "review"
19+
state, where contributors can require changes, reject or accept the
20+
pull request. At any time, you can also "update" the pull request, which
21+
will result in another Travis run.
22+
23+
.. image:: /_images/components/workflow/pull_request.png
24+
25+
Below is the configuration for the pull request state machine.
26+
27+
.. configuration-block::
28+
29+
.. code-block:: yaml
30+
31+
# app/config/config.yml
32+
framework:
33+
workflows:
34+
pull_request:
35+
type: 'state_machine'
36+
supports:
37+
- AppBundle\Entity\PullRequest
38+
places:
39+
- start
40+
- coding
41+
- travis
42+
- review
43+
- merged
44+
- closed
45+
transitions:
46+
submit:
47+
from: start
48+
to: travis
49+
update:
50+
from: [coding, travis, review]
51+
to: travis
52+
wait_for_reivew:
53+
from: travis
54+
to: review
55+
request_change:
56+
from: review
57+
to: coding
58+
accept:
59+
from: review
60+
to: merged
61+
reject:
62+
from: review
63+
to: closed
64+
reopen:
65+
from: closed
66+
to: review
67+
68+
.. code-block:: xml
69+
70+
<!-- app/config/config.xml -->
71+
<?xml version="1.0" encoding="utf-8" ?>
72+
<container xmlns="http://symfony.com/schema/dic/services"
73+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
74+
xmlns:framework="http://symfony.com/schema/dic/symfony"
75+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
76+
http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"
77+
>
78+
79+
<framework:config>
80+
<framework:workflow name="pull_request" type="state_machine">
81+
<framework:marking-store type="single_state"/>
82+
83+
<framework:support>AppBundle\Entity\PullRequest</framework:support>
84+
85+
<framework:place>start</framework:place>
86+
<framework:place>coding</framework:place>
87+
<framework:place>travis</framework:place>
88+
<framework:place>review</framework:place>
89+
<framework:place>merged</framework:place>
90+
<framework:place>closed</framework:place>
91+
92+
<framework:transition name="submit">
93+
<framework:from>start</framework:from>
94+
95+
<framework:to>travis</framework:to>
96+
</framework:transition>
97+
98+
<framework:transition name="update">
99+
<framework:from>coding</framework:from>
100+
<framework:from>travis</framework:from>
101+
<framework:from>review</framework:from>
102+
103+
<framework:to>travis</framework:to>
104+
</framework:transition>
105+
106+
<framework:transition name="wait_for_review">
107+
<framework:from>travis</framework:from>
108+
109+
<framework:to>review</framework:to>
110+
</framework:transition>
111+
112+
<framework:transition name="request_change">
113+
<framework:from>review</framework:from>
114+
115+
<framework:to>coding</framework:to>
116+
</framework:transition>
117+
118+
<framework:transition name="accept">
119+
<framework:from>review</framework:from>
120+
121+
<framework:to>merged</framework:to>
122+
</framework:transition>
123+
124+
<framework:transition name="reject">
125+
<framework:from>review</framework:from>
126+
127+
<framework:to>closed</framework:to>
128+
</framework:transition>
129+
130+
<framework:transition name="reopen">
131+
<framework:from>closed</framework:from>
132+
133+
<framework:to>review</framework:to>
134+
</framework:transition>
135+
136+
</framework:workflow>
137+
138+
</framework:config>
139+
</container>
140+
141+
.. code-block:: php
142+
143+
// app/config/config.php
144+
145+
$container->loadFromExtension('framework', array(
146+
// ...
147+
'workflows' => array(
148+
'pull_request' => array(
149+
'type' => 'state_machine',
150+
'supports' => array('AppBundle\Entity\PullRequest'),
151+
'places' => array(
152+
'start',
153+
'coding',
154+
'travis',
155+
'review',
156+
'merged',
157+
'closed',
158+
),
159+
'transitions' => array(
160+
'start'=> array(
161+
'form' => 'start',
162+
'to' => 'travis',
163+
),
164+
'update'=> array(
165+
'form' => array('coding','travis','review'),
166+
'to' => 'travis',
167+
),
168+
'wait_for_reivew'=> array(
169+
'form' => 'travis',
170+
'to' => 'review',
171+
),
172+
'request_change'=> array(
173+
'form' => 'review',
174+
'to' => 'coding',
175+
),
176+
'accept'=> array(
177+
'form' => 'review',
178+
'to' => 'merged',
179+
),
180+
'reject'=> array(
181+
'form' => 'review',
182+
'to' => 'closed',
183+
),
184+
'reopen'=> array(
185+
'form' => 'start',
186+
'to' => 'review',
187+
),
188+
),
189+
),
190+
),
191+
));
192+
193+
You can now use this state machine by getting the ``state_machine.pull_request`` service::
194+
195+
$stateMachine = $this->container->get('state_machine.pull_request');
196+
197+
.. _Petri net: https://en.wikipedia.org/wiki/Petri_net

0 commit comments

Comments
 (0)