-
-
Notifications
You must be signed in to change notification settings - Fork 5.2k
[WIP] Improvements to Workflow documentation #10757
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,9 +16,13 @@ to a property of the object to remember the current place. | |
The terminology above is commonly used when discussing workflows and | ||
`Petri nets`_ | ||
|
||
The Workflow component does also support state machines. A state machine is a subset | ||
of a workflow and its purpose is to hold a state of your model. Read more about the | ||
differences and specific features of state machine in :doc:`/workflow/state-machines`. | ||
The Workflow component helps you handle different kind of workflows in your application. | ||
|
||
.. tip:: | ||
|
||
Component does also support state machines. A state machine is a subset | ||
of a workflow and its purpose is to hold a state of your model. Read more about the | ||
differences and specific features of state machine in :doc:`/workflow/state-machines`. | ||
|
||
Examples | ||
-------- | ||
|
@@ -41,13 +45,300 @@ By defining a workflow like this, there is an overview how the process looks lik | |
logic is not mixed with the controllers, models or view. The order of the steps can be changed | ||
by changing the configuration only. | ||
|
||
|
||
Installation | ||
------------ | ||
|
||
In applications using :doc:`Symfony Flex </setup/flex>`, run this command to | ||
install the workflow feature before using it: | ||
|
||
.. code-block:: terminal | ||
|
||
$ composer require symfony/workflow | ||
|
||
Defining a Workflow | ||
------------------- | ||
|
||
Consider the following example for a blog post that can have these places: | ||
``draft``, ``review``, ``rejected``, ``published``. You can define the workflow | ||
like this: | ||
|
||
.. configuration-block:: | ||
|
||
.. code-block:: yaml | ||
|
||
# config/packages/workflow.yaml | ||
framework: | ||
workflows: | ||
blog_publishing: | ||
type: 'workflow' # or 'state_machine' | ||
audit_trail: | ||
enabled: true | ||
marking_store: | ||
type: 'single_state' # or 'multiple_state' | ||
arguments: | ||
- 'currentPlace' | ||
supports: | ||
- App\Entity\BlogPost | ||
initial_place: draft | ||
places: | ||
- draft | ||
- review | ||
- rejected | ||
- published | ||
transitions: | ||
to_review: | ||
from: draft | ||
to: review | ||
publish: | ||
from: review | ||
to: published | ||
reject: | ||
from: review | ||
to: rejected | ||
|
||
.. code-block:: xml | ||
|
||
<!-- config/packages/workflow.xml --> | ||
<?xml version="1.0" encoding="utf-8" ?> | ||
<container xmlns="http://symfony.com/schema/dic/services" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xmlns:framework="http://symfony.com/schema/dic/symfony" | ||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd | ||
http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd" | ||
> | ||
|
||
<framework:config> | ||
<framework:workflow name="blog_publishing" type="workflow"> | ||
<framework:audit-trail enabled="true" /> | ||
|
||
<framework:marking-store type="single_state"> | ||
<framework:argument>currentPlace</framework:argument> | ||
</framework:marking-store> | ||
|
||
<framework:support>App\Entity\BlogPost</framework:support> | ||
|
||
<framework:place>draft</framework:place> | ||
<framework:place>review</framework:place> | ||
<framework:place>rejected</framework:place> | ||
<framework:place>published</framework:place> | ||
|
||
<framework:transition name="to_review"> | ||
<framework:from>draft</framework:from> | ||
|
||
<framework:to>review</framework:to> | ||
</framework:transition> | ||
|
||
<framework:transition name="publish"> | ||
<framework:from>review</framework:from> | ||
|
||
<framework:to>published</framework:to> | ||
</framework:transition> | ||
|
||
<framework:transition name="reject"> | ||
<framework:from>review</framework:from> | ||
|
||
<framework:to>rejected</framework:to> | ||
</framework:transition> | ||
|
||
</framework:workflow> | ||
|
||
</framework:config> | ||
</container> | ||
|
||
.. code-block:: php | ||
|
||
// config/packages/workflow.php | ||
|
||
$container->loadFromExtension('framework', array( | ||
// ... | ||
'workflows' => array( | ||
'blog_publishing' => array( | ||
'type' => 'workflow', // or 'state_machine' | ||
'audit_trail' => array( | ||
'enabled' => true | ||
), | ||
'marking_store' => array( | ||
'type' => 'single_state', // or 'multiple_state' | ||
'arguments' => array('currentPlace') | ||
), | ||
'supports' => array('App\Entity\BlogPost'), | ||
'places' => array( | ||
'draft', | ||
'review', | ||
'rejected', | ||
'published', | ||
), | ||
'transitions' => array( | ||
'to_review' => array( | ||
'from' => 'draft', | ||
'to' => 'review', | ||
), | ||
'publish' => array( | ||
'from' => 'review', | ||
'to' => 'published', | ||
), | ||
'reject' => array( | ||
'from' => 'review', | ||
'to' => 'rejected', | ||
), | ||
), | ||
), | ||
), | ||
)); | ||
|
||
.. code-block:: php | ||
|
||
class BlogPost | ||
{ | ||
public $currentPlace; // This property is used by the marking store | ||
public $title; | ||
public $content; | ||
} | ||
|
||
.. note:: | ||
|
||
The marking store type could be "single_state" or "multiple_state". | ||
A multiple state marking store allow your model to be on multiple places | ||
at the same time. | ||
|
||
.. tip:: | ||
|
||
The ``type`` (default value ``single_state``) and ``arguments`` (default | ||
value ``marking``) attributes of the ``marking_store`` option are optional. | ||
If omitted, their default values will be used. | ||
|
||
.. tip:: | ||
|
||
Setting the ``audit_trail.enabled`` option to ``true`` makes the application | ||
generate detailed log messages for the workflow activity. | ||
|
||
.. tip:: | ||
|
||
You can easily visualize your workflow by using ``workflow:dump`` command. | ||
Read more on :doc:`/workflow/dumping-workflows` | ||
|
||
|
||
Workflow Events | ||
--------------- | ||
|
||
To make your workflows more flexible, Workflow component allows you to listen on | ||
several events raised during transitions. You can create event listeners to block them | ||
(i.e. depending on the data in the blog post) and do additional actions when a workflow | ||
operation happened (e.g. sending announcements). Read more on different kind of events | ||
on :doc:`/workflow/events` | ||
|
||
|
||
Using a Workflow | ||
---------------- | ||
|
||
Once the ``blog_publishing`` workflow has been defined, you can now use it to | ||
decide what actions are allowed on a blog post. For example, inside a controller | ||
of an application using the :ref:`default services.yaml configuration <service-container-services-load-example>`, | ||
you can get the workflow by injecting the Workflow registry service:: | ||
|
||
// ... | ||
use Symfony\Component\Workflow\Registry; | ||
use App\Entity\BlogPost; | ||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; | ||
use Symfony\Component\Workflow\Exception\TransitionException; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please use ordered use statements |
||
|
||
class BlogController extends AbstractController | ||
{ | ||
public function edit(Registry $workflows) | ||
{ | ||
$post = new BlogPost(); | ||
$workflow = $workflows->get($post); | ||
|
||
// if there are multiple workflows for the same class, | ||
// pass the workflow name as the second argument | ||
// $workflow = $workflows->get($post, 'blog_publishing'); | ||
|
||
// you can also get all workflows associated with an object, which is useful | ||
// for example to show the status of all those workflows in a backend | ||
$postWorkflows = $workflows->all($post); | ||
|
||
$workflow->can($post, 'publish'); // False | ||
$workflow->can($post, 'to_review'); // True | ||
|
||
// Update the currentPlace on the post | ||
try { | ||
$workflow->apply($post, 'to_review'); | ||
} catch (TransitionException $exception) { | ||
// ... if the transition is not allowed | ||
} | ||
|
||
// See all the available transitions for the post in the current state | ||
$transitions = $workflow->getEnabledTransitions($post); | ||
} | ||
} | ||
|
||
.. versionadded:: 4.1 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please add a blankline after the versionadded directive |
||
The :class:`Symfony\\Component\\Workflow\\Exception\\TransitionException` | ||
class was introduced in Symfony 4.1. | ||
|
||
.. versionadded:: 4.1 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please add a blankline after the versionadded directive |
||
The :method:`Symfony\\Component\\Workflow\\Registry::all` method was | ||
introduced in Symfony 4.1. | ||
|
||
|
||
Usage in Twig | ||
------------- | ||
|
||
Symfony defines several Twig functions to manage workflows and reduce the need | ||
of domain logic in your templates: | ||
|
||
``workflow_can()`` | ||
Returns ``true`` if the given object can make the given transition. | ||
|
||
``workflow_transitions()`` | ||
Returns an array with all the transitions enabled for the given object. | ||
|
||
``workflow_marked_places()`` | ||
Returns an array with the place names of the given marking. | ||
|
||
``workflow_has_marked_place()`` | ||
Returns ``true`` if the marking of the given object has the given state. | ||
|
||
The following example shows these functions in action: | ||
|
||
.. code-block:: twig | ||
|
||
<h3>Actions</h3> | ||
{% if workflow_can(post, 'publish') %} | ||
<a href="...">Publish article</a> | ||
{% endif %} | ||
{% if workflow_can(post, 'to_review') %} | ||
<a href="...">Submit to review</a> | ||
{% endif %} | ||
{% if workflow_can(post, 'reject') %} | ||
<a href="...">Reject article</a> | ||
{% endif %} | ||
|
||
{# Or loop through the enabled transitions #} | ||
{% for transition in workflow_transitions(post) %} | ||
<a href="...">{{ transition.name }}</a> | ||
{% else %} | ||
No actions available. | ||
{% endfor %} | ||
|
||
{# Check if the object is in some specific place #} | ||
{% if workflow_has_marked_place(post, 'review') %} | ||
<p>This post is ready for review.</p> | ||
{% endif %} | ||
|
||
{# Check if some place has been marked on the object #} | ||
{% if 'waiting_some_approval' in workflow_marked_places(post) %} | ||
<span class="label">PENDING</span> | ||
{% endif %} | ||
|
||
Learn more | ||
---------- | ||
|
||
.. toctree:: | ||
:maxdepth: 1 | ||
|
||
workflow/usage | ||
workflow/events | ||
workflow/state-machines | ||
workflow/dumping-workflows | ||
|
||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -12,8 +12,6 @@ Use the ``GraphvizDumper`` or ``StateMachineGraphvizDumper`` to create DOT | |||||
files, or use ``PlantUmlDumper`` for PlantUML files. Both types can be converted | ||||||
to PNG or SVG images. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
Images of the workflow defined above: | ||||||
|
||||||
.. code-block:: php | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
// dump-graph-dot.php | ||||||
|
@@ -34,6 +32,15 @@ Images of the workflow defined above: | |||||
# run this command if you prefer SVG images: | ||||||
# $ php dump-graph-dot.php | dot -Tsvg -o dot_graph.svg | ||||||
|
||||||
|
||||||
Inside a Symfony application, you can dump the files with those commands using | ||||||
``workflow:dump`` command: | ||||||
|
||||||
.. code-block:: terminal | ||||||
|
||||||
$ php bin/console workflow:dump name | dot -Tsvg -o graph.svg | ||||||
$ php bin/console workflow:dump name --dump-format=puml | java -jar plantuml.jar -p > workflow.png | ||||||
|
||||||
The DOT result will look like this: | ||||||
|
||||||
.. image:: /_images/components/workflow/blogpost.png | ||||||
|
@@ -42,13 +49,7 @@ The PUML result: | |||||
|
||||||
.. image:: /_images/components/workflow/blogpost_puml.png | ||||||
|
||||||
Inside a Symfony application, you can dump the files with those commands using | ||||||
``workflow:dump`` command: | ||||||
|
||||||
.. code-block:: terminal | ||||||
|
||||||
$ php bin/console workflow:dump name | dot -Tsvg -o graph.svg | ||||||
$ php bin/console workflow:dump name --dump-format=puml | java -jar plantuml.jar -p > workflow.png | ||||||
|
||||||
.. note:: | ||||||
|
||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it could be usefull to explain why a developer should/must/can use this component inside its project.
With a sentence like : Use case: if in your project you are using a $status on an object, with notions of progression/validation, this component can be a solid foundation/wrapper.
wdyt?