Skip to content

Commit 5a2074b

Browse files
Workflow doc
1 parent 6710730 commit 5a2074b

File tree

5 files changed

+134
-89
lines changed

5 files changed

+134
-89
lines changed
181 Bytes
Loading

components/workflow.rst

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,16 +76,19 @@ are trying to use it with::
7676
Usage
7777
-----
7878

79-
When you have configured a ``Registry`` with your workflows, you may use it as follows::
79+
When you have configured a ``Registry`` with your workflows,
80+
you can retreive a workflow from it and use it as follows::
8081

8182
// ...
82-
$post = new BlogPost();
8383
$workflow = $registry->get($post);
8484

85+
$post = new BlogPost(); // consider that $post is in state "draft" by default
86+
8587
$workflow->can($post, 'publish'); // False
8688
$workflow->can($post, 'to_review'); // True
8789

88-
$workflow->apply($post, 'to_review');
90+
$workflow->apply($post, 'to_review'); // $post is now in state "review"
91+
8992
$workflow->can($post, 'publish'); // True
9093
$workflow->getEnabledTransitions($post); // ['publish', 'reject']
9194

workflow.rst

Lines changed: 103 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,10 @@ As configured, the following property is used by the marking store::
197197
With this workflow named ``blog_publishing``, you can get help to decide
198198
what actions are allowed on a blog post::
199199

200-
$post = new App\Entity\BlogPost();
200+
use Symfony\Component\Workflow\Exception\LogicException;
201+
use App\Entity\BlogPost;
202+
203+
$post = BlogPost();
201204

202205
$workflow = $this->container->get('workflow.blog_publishing');
203206
$workflow->can($post, 'publish'); // False
@@ -401,6 +404,9 @@ This means that each event has access to the following information:
401404
:method:`Symfony\\Component\\Workflow\\Event\\Event::getWorkflowName`
402405
Returns a string with the name of the workflow that triggered the event.
403406

407+
:method:`Symfony\\Component\\Workflow\\Event\\Event::getMetadata`
408+
Returns a metadata.
409+
404410
For Guard Events, there is an extended class :class:`Symfony\\Component\\Workflow\\Event\\GuardEvent`.
405411
This class has two more methods:
406412

@@ -410,6 +416,13 @@ This class has two more methods:
410416
:method:`Symfony\\Component\\Workflow\\Event\\GuardEvent::setBlocked`
411417
Sets the blocked value.
412418

419+
:method:`Symfony\\Component\\Workflow\\Event\\GuardEvent::getTransitionBlockerList`
420+
Returns the event :class:`Symfony\\Component\\Workflow\\TransitionBlockerList`.
421+
See :ref:`blocking transitions <workflow-blocking-transitions>`.
422+
423+
:method:`Symfony\\Component\\Workflow\\Event\\GuardEvent::addTransitionBlocker`
424+
Addes a :class:`Symfony\\Component\\Workflow\\TransitionBlocker`.
425+
413426
.. _workflow-blocking-transitions:
414427

415428
Blocking Transitions
@@ -438,16 +451,61 @@ transition. The value of this option is any valid expression created with the
438451
from: draft
439452
to: reviewed
440453
publish:
441-
# or "is_anonymous", "is_remember_me", "is_fully_authenticated", "is_granted"
454+
# or "is_anonymous", "is_remember_me", "is_fully_authenticated", "is_granted", "is_valid"
442455
guard: "is_authenticated"
443456
from: reviewed
444457
to: published
445458
reject:
446-
# or any valid expression language with "subject" referring to the post
447-
guard: "has_role('ROLE_ADMIN') and subject.isStatusReviewed()"
459+
# or any valid expression language with "subject" referring to the supported object
460+
guard: "has_role('ROLE_ADMIN') and subject.isRejectable()"
448461
from: reviewed
449462
to: rejected
450463
464+
You can also use transition blockers to block and return a user-friendly error
465+
message when you stop a transition from happening.
466+
In the example we get this message from the
467+
:class:`Symfony\\Component\\Workflow\\Event\\Event`'s metadata, giving you a
468+
central place to manage the text.
469+
470+
This example has been simplified; in production you may prefer to use the
471+
:doc:`Translation </components/translation>` component to manage messages in one
472+
place::
473+
474+
namespace App\Listener\Workflow\Task;
475+
476+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
477+
use Symfony\Component\Workflow\Event\GuardEvent;
478+
use Symfony\Component\Workflow\TransitionBlocker;
479+
480+
class BlogPostPublishListener implements EventSubscriberInterface
481+
{
482+
public function guardPublish(GuardEvent $event)
483+
{
484+
$eventTransition = $event->getTransition();
485+
$hourLimit = $event->getMetadata('hour_limit', $eventTransition);
486+
487+
if (date('H') <= $hourLimit) {
488+
return;
489+
}
490+
491+
// Block the transition "publish" if it is more than 8 PM
492+
// with the message for end user
493+
$explanation = $event->getMetadata('explanation', $eventTransition);
494+
$event->addTransitionBlocker(new TransitionBlocker($explanation , 0));
495+
}
496+
497+
public static function getSubscribedEvents()
498+
{
499+
return [
500+
'workflow.blog_publishing.guard.publish' => ['guardPublish'],
501+
];
502+
}
503+
}
504+
505+
.. versionadded:: 4.1
506+
507+
The transition blockers were introduced in Symfony 4.1.
508+
451509
Usage in Twig
452510
-------------
453511

@@ -470,15 +528,15 @@ The following example shows these functions in action:
470528

471529
.. code-block:: html+twig
472530

473-
<h3>Actions</h3>
531+
<h3>Actions on Blog Post</h3>
474532
{% if workflow_can(post, 'publish') %}
475-
<a href="...">Publish article</a>
533+
<a href="...">Publish</a>
476534
{% endif %}
477535
{% if workflow_can(post, 'to_review') %}
478536
<a href="...">Submit to review</a>
479537
{% endif %}
480538
{% if workflow_can(post, 'reject') %}
481-
<a href="...">Reject article</a>
539+
<a href="...">Reject</a>
482540
{% endif %}
483541

484542
{# Or loop through the enabled transitions #}
@@ -494,8 +552,8 @@ The following example shows these functions in action:
494552
{% endif %}
495553

496554
{# Check if some place has been marked on the object #}
497-
{% if 'waiting_some_approval' in workflow_marked_places(post) %}
498-
<span class="label">PENDING</span>
555+
{% if 'reviewed' in workflow_marked_places(post) %}
556+
<span class="label">Reviewed</span>
499557
{% endif %}
500558

501559
Storing Metadata
@@ -532,6 +590,12 @@ requires:
532590
to: review
533591
metadata:
534592
priority: 0.5
593+
publish:
594+
from: reviewed
595+
to: published
596+
metadata:
597+
hour_limit: 20
598+
explanation: 'You can not publish after 8 PM.'
535599
# ...
536600
537601
.. code-block:: xml
@@ -563,6 +627,14 @@ requires:
563627
<framework:priority>0.5</framework:priority>
564628
</framework:metadata>
565629
</framework:transition>
630+
<framework:transition name="publish">
631+
<framework:from>reviewed</framework:from>
632+
<framework:to>published</framework:to>
633+
<framework:metadata>
634+
<framework:hour_limit>20</framework:priority>
635+
<framework:explanation>You can not publish after 8 PM.</framework:priority>
636+
</framework:metadata>
637+
</framework:transition>
566638
<!-- ... -->
567639
</framework:workflow>
568640
</framework:config>
@@ -595,6 +667,15 @@ requires:
595667
'priority' => 0.5,
596668
],
597669
],
670+
'publish' => [
671+
'from' => 'reviewed',
672+
'to' => 'published',
673+
'metadata' => [
674+
'hour_limit' => 20,
675+
'explanation' => 'You can not publish after 8 PM.',
676+
],
677+
],
678+
// ...
598679
],
599680
],
600681
],
@@ -603,25 +684,26 @@ requires:
603684
Then you can access this metadata in your controller as follows::
604685

605686
use Symfony\Component\Workflow\Registry;
687+
use App\Entity\BlogPost;
606688

607-
public function myController(Registry $registry, Article $article)
689+
public function myController(Registry $registry, BlogPost $post)
608690
{
609-
$workflow = $registry->get($article);
691+
$workflow = $registry->get($post);
610692

611693
$title = $workflow
612694
->getMetadataStore()
613-
->getWorkflowMetadata()['title'] ?? false
695+
->getWorkflowMetadata()['title'] ?? 'Default title'
614696
;
615697

616698
// or
617699
$aTransition = $workflow->getDefinition()->getTransitions()[0];
618700
$transitionTitle = $workflow
619701
->getMetadataStore()
620-
->getTransitionMetadata($aTransition)['title'] ?? false
702+
->getTransitionMetadata($aTransition)['priority'] ?? 0
621703
;
622704
}
623705

624-
There is a shortcut that works with everything::
706+
There is a shortcut that works with every metadata level::
625707

626708
$title = $workflow->getMetadataStore()->getMetadata('title');
627709

@@ -633,76 +715,35 @@ In a :ref:`flash message <flash-messages>` in your controller::
633715
$title = $workflow->getMetadataStore()->getMetadata('title', $transition);
634716
$this->addFlash('info', "You have successfully applied the transition with title: '$title'");
635717

636-
Metadata can also be accessed in a Listener, from the Event object.
637-
638-
Using transition blockers you can return a user-friendly error message when you
639-
stop a transition from happening. In the example we get this message from the
640-
:class:`Symfony\\Component\\Workflow\\Event\\Event`'s metadata, giving you a
641-
central place to manage the text.
642-
643-
This example has been simplified; in production you may prefer to use the
644-
:doc:`Translation </components/translation>` component to manage messages in one
645-
place::
646-
647-
namespace App\Listener\Workflow\Task;
648-
649-
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
650-
use Symfony\Component\Workflow\Event\GuardEvent;
651-
use Symfony\Component\Workflow\TransitionBlocker;
652-
653-
class OverdueGuard implements EventSubscriberInterface
654-
{
655-
public function guardPublish(GuardEvent $event)
656-
{
657-
$timeLimit = $event->getMetadata('time_limit', $event->getTransition());
658-
659-
if (date('Hi') <= $timeLimit) {
660-
return;
661-
}
662-
663-
$explanation = $event->getMetadata('explanation', $event->getTransition());
664-
$event->addTransitionBlocker(new TransitionBlocker($explanation , 0));
665-
}
666-
667-
public static function getSubscribedEvents()
668-
{
669-
return [
670-
'workflow.task.guard.done' => 'guardPublish',
671-
];
672-
}
673-
}
674-
675-
.. versionadded:: 4.1
676-
677-
The transition blockers were introduced in Symfony 4.1.
718+
Metadata can also be accessed in a Listener, from the :class:`Symfony\\Component\\Workflow\\Event\\Event` object.
678719

679720
In Twig templates, metadata is available via the ``workflow_metadata()`` function:
680721

681722
.. code-block:: html+twig
682723

683-
<h2>Metadata</h2>
724+
<h2>Metadata of Blog Post</h2>
684725
<p>
685726
<strong>Workflow</strong>:<br >
686-
<code>{{ workflow_metadata(article, 'title') }}</code>
727+
<code>{{ workflow_metadata(blog_post, 'title') }}</code>
687728
</p>
688729
<p>
689730
<strong>Current place(s)</strong>
690731
<ul>
691-
{% for place in workflow_marked_places(article) %}
732+
{% for place in workflow_marked_places(blog_post) %}
692733
<li>
693734
{{ place }}:
694-
<code>{{ workflow_metadata(article, 'max_num_of_words', place) ?: 'Unlimited'}}</code>
735+
<code>{{ workflow_metadata(blog_post, 'max_num_of_words', place) ?: 'Unlimited'}}</code>
695736
</li>
696737
{% endfor %}
697738
</ul>
698739
</p>
699740
<p>
700741
<strong>Enabled transition(s)</strong>
701742
<ul>
702-
{% for transition in workflow_transitions(article) %}
743+
{% for transition in workflow_transitions(blog_post) %}
703744
<li>
704745
{{ transition.name }}:
705-
<code>{{ workflow_metadata(article, 'priority', transition) ?: '0' }}</code>
746+
<code>{{ workflow_metadata(blog_post, 'priority', transition) ?: '0' }}</code>
706747
</li>
707748
{% endfor %}
708749
</ul>

workflow/dumping-workflows.rst

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ How to Dump Workflows
55
=====================
66

77
To help you debug your workflows, you can dump a representation of your workflow
8-
or state machine with the use of a ``DumperInterface``. Symfony provides 2
9-
different dumpers both based on Dot.
8+
or state machine with the use of a ``DumperInterface``. Symfony provides two
9+
different dumpers, both based on Dot (see below).
1010

1111
Use the ``GraphvizDumper`` or ``StateMachineGraphvizDumper`` to create DOT
1212
files, or use ``PlantUmlDumper`` for PlantUML files. Both types can be converted
@@ -25,10 +25,9 @@ Images of the workflow defined above::
2525
.. code-block:: terminal
2626
2727
$ php dump-graph-dot.php | dot -Tpng -o dot_graph.png
28-
$ php dump-graph-puml.php | java -jar plantuml.jar -p > puml_graph.png
29-
3028
# run this command if you prefer SVG images:
3129
# $ php dump-graph-dot.php | dot -Tsvg -o dot_graph.svg
30+
$ php dump-graph-puml.php | java -jar plantuml.jar -p > puml_graph.png
3231
3332
The DOT result will look like this:
3433

@@ -43,8 +42,10 @@ Inside a Symfony application, you can dump the files with those commands using
4342

4443
.. code-block:: terminal
4544
46-
$ php bin/console workflow:dump name | dot -Tsvg -o graph.svg
47-
$ php bin/console workflow:dump name --dump-format=puml | java -jar plantuml.jar -p > workflow.png
45+
$ php bin/console workflow:dump workflow_name | dot -Tpng -o workflow_name.png
46+
# run this command if you prefer SVG images:
47+
# $ php bin/console workflow:dump workflow_name | dot -Tsvg -o workflow_name.svg
48+
$ php bin/console workflow:dump workflow_name --dump-format=puml | java -jar plantuml.jar -p > workflow_name.png
4849
4950
.. note::
5051

0 commit comments

Comments
 (0)