diff --git a/_images/components/workflow/pull_request.png b/_images/components/workflow/pull_request.png index 1aa8886728c..692a95345ae 100644 Binary files a/_images/components/workflow/pull_request.png and b/_images/components/workflow/pull_request.png differ diff --git a/components/workflow.rst b/components/workflow.rst index bfa441f3827..3d3ac24cc2c 100644 --- a/components/workflow.rst +++ b/components/workflow.rst @@ -76,16 +76,19 @@ are trying to use it with:: Usage ----- -When you have configured a ``Registry`` with your workflows, you may use it as follows:: +When you have configured a ``Registry`` with your workflows, +you can retreive a workflow from it and use it as follows:: // ... + // Consider that $post is in state "draft" by default $post = new BlogPost(); $workflow = $registry->get($post); $workflow->can($post, 'publish'); // False $workflow->can($post, 'to_review'); // True - $workflow->apply($post, 'to_review'); + $workflow->apply($post, 'to_review'); // $post is now in state "review" + $workflow->can($post, 'publish'); // True $workflow->getEnabledTransitions($post); // ['publish', 'reject'] diff --git a/workflow.rst b/workflow.rst index 493388249da..5624f0785a0 100644 --- a/workflow.rst +++ b/workflow.rst @@ -197,7 +197,10 @@ As configured, the following property is used by the marking store:: With this workflow named ``blog_publishing``, you can get help to decide what actions are allowed on a blog post:: - $post = new App\Entity\BlogPost(); + use Symfony\Component\Workflow\Exception\LogicException; + use App\Entity\BlogPost; + + $post = BlogPost(); $workflow = $this->container->get('workflow.blog_publishing'); $workflow->can($post, 'publish'); // False @@ -401,6 +404,9 @@ This means that each event has access to the following information: :method:`Symfony\\Component\\Workflow\\Event\\Event::getWorkflowName` Returns a string with the name of the workflow that triggered the event. +:method:`Symfony\\Component\\Workflow\\Event\\Event::getMetadata` + Returns a metadata. + For Guard Events, there is an extended class :class:`Symfony\\Component\\Workflow\\Event\\GuardEvent`. This class has two more methods: @@ -410,6 +416,13 @@ This class has two more methods: :method:`Symfony\\Component\\Workflow\\Event\\GuardEvent::setBlocked` Sets the blocked value. +:method:`Symfony\\Component\\Workflow\\Event\\GuardEvent::getTransitionBlockerList` + Returns the event :class:`Symfony\\Component\\Workflow\\TransitionBlockerList`. + See :ref:`blocking transitions `. + +:method:`Symfony\\Component\\Workflow\\Event\\GuardEvent::addTransitionBlocker` + Add a :class:`Symfony\\Component\\Workflow\\TransitionBlocker` instance. + .. _workflow-blocking-transitions: Blocking Transitions @@ -438,16 +451,61 @@ transition. The value of this option is any valid expression created with the from: draft to: reviewed publish: - # or "is_anonymous", "is_remember_me", "is_fully_authenticated", "is_granted" + # or "is_anonymous", "is_remember_me", "is_fully_authenticated", "is_granted", "is_valid" guard: "is_authenticated" from: reviewed to: published reject: - # or any valid expression language with "subject" referring to the post - guard: "has_role('ROLE_ADMIN') and subject.isStatusReviewed()" + # or any valid expression language with "subject" referring to the supported object + guard: "has_role('ROLE_ADMIN') and subject.isRejectable()" from: reviewed to: rejected +You can also use transition blockers to block and return a user-friendly error +message when you stop a transition from happening. +In the example we get this message from the +:class:`Symfony\\Component\\Workflow\\Event\\Event`'s metadata, giving you a +central place to manage the text. + +This example has been simplified; in production you may prefer to use the +:doc:`Translation ` component to manage messages in one +place:: + + namespace App\Listener\Workflow\Task; + + use Symfony\Component\EventDispatcher\EventSubscriberInterface; + use Symfony\Component\Workflow\Event\GuardEvent; + use Symfony\Component\Workflow\TransitionBlocker; + + class BlogPostPublishListener implements EventSubscriberInterface + { + public function guardPublish(GuardEvent $event) + { + $eventTransition = $event->getTransition(); + $hourLimit = $event->getMetadata('hour_limit', $eventTransition); + + if (date('H') <= $hourLimit) { + return; + } + + // Block the transition "publish" if it is more than 8 PM + // with the message for end user + $explanation = $event->getMetadata('explanation', $eventTransition); + $event->addTransitionBlocker(new TransitionBlocker($explanation , 0)); + } + + public static function getSubscribedEvents() + { + return [ + 'workflow.blog_publishing.guard.publish' => ['guardPublish'], + ]; + } + } + +.. versionadded:: 4.1 + + The transition blockers were introduced in Symfony 4.1. + Usage in Twig ------------- @@ -470,15 +528,15 @@ The following example shows these functions in action: .. code-block:: html+twig -

Actions

+

Actions on Blog Post

{% if workflow_can(post, 'publish') %} - Publish article + Publish {% endif %} {% if workflow_can(post, 'to_review') %} Submit to review {% endif %} {% if workflow_can(post, 'reject') %} - Reject article + Reject {% endif %} {# Or loop through the enabled transitions #} @@ -494,8 +552,8 @@ The following example shows these functions in action: {% endif %} {# Check if some place has been marked on the object #} - {% if 'waiting_some_approval' in workflow_marked_places(post) %} - PENDING + {% if 'reviewed' in workflow_marked_places(post) %} + Reviewed {% endif %} Storing Metadata @@ -532,7 +590,12 @@ requires: to: review metadata: priority: 0.5 - # ... + publish: + from: reviewed + to: published + metadata: + hour_limit: 20 + explanation: 'You can not publish after 8 PM.' .. code-block:: xml @@ -563,7 +626,14 @@ requires: 0.5 - + + reviewed + published + + 20 + You can not publish after 8 PM. + + @@ -595,6 +665,14 @@ requires: 'priority' => 0.5, ], ], + 'publish' => [ + 'from' => 'reviewed', + 'to' => 'published', + 'metadata' => [ + 'hour_limit' => 20, + 'explanation' => 'You can not publish after 8 PM.', + ], + ], ], ], ], @@ -603,27 +681,29 @@ requires: Then you can access this metadata in your controller as follows:: use Symfony\Component\Workflow\Registry; + use App\Entity\BlogPost; - public function myController(Registry $registry, Article $article) + public function myController(Registry $registry, BlogPost $post) { - $workflow = $registry->get($article); + $workflow = $registry->get($post); $title = $workflow ->getMetadataStore() - ->getWorkflowMetadata()['title'] ?? false + ->getWorkflowMetadata()['title'] ?? 'Default title' ; // or $aTransition = $workflow->getDefinition()->getTransitions()[0]; $transitionTitle = $workflow ->getMetadataStore() - ->getTransitionMetadata($aTransition)['title'] ?? false + ->getTransitionMetadata($aTransition)['priority'] ?? 0 ; } -There is a shortcut that works with everything:: +There is a shortcut that works with every metadata level:: $title = $workflow->getMetadataStore()->getMetadata('title'); + $priority = $workflow->getMetadataStore()->getMetadata('priority'); In a :ref:`flash message ` in your controller:: @@ -633,65 +713,24 @@ In a :ref:`flash message ` in your controller:: $title = $workflow->getMetadataStore()->getMetadata('title', $transition); $this->addFlash('info', "You have successfully applied the transition with title: '$title'"); -Metadata can also be accessed in a Listener, from the Event object. - -Using transition blockers you can return a user-friendly error message when you -stop a transition from happening. In the example we get this message from the -:class:`Symfony\\Component\\Workflow\\Event\\Event`'s metadata, giving you a -central place to manage the text. - -This example has been simplified; in production you may prefer to use the -:doc:`Translation ` component to manage messages in one -place:: - - namespace App\Listener\Workflow\Task; - - use Symfony\Component\EventDispatcher\EventSubscriberInterface; - use Symfony\Component\Workflow\Event\GuardEvent; - use Symfony\Component\Workflow\TransitionBlocker; - - class OverdueGuard implements EventSubscriberInterface - { - public function guardPublish(GuardEvent $event) - { - $timeLimit = $event->getMetadata('time_limit', $event->getTransition()); - - if (date('Hi') <= $timeLimit) { - return; - } - - $explanation = $event->getMetadata('explanation', $event->getTransition()); - $event->addTransitionBlocker(new TransitionBlocker($explanation , 0)); - } - - public static function getSubscribedEvents() - { - return [ - 'workflow.task.guard.done' => 'guardPublish', - ]; - } - } - -.. versionadded:: 4.1 - - The transition blockers were introduced in Symfony 4.1. +Metadata can also be accessed in a Listener, from the :class:`Symfony\\Component\\Workflow\\Event\\Event` object. In Twig templates, metadata is available via the ``workflow_metadata()`` function: .. code-block:: html+twig -

Metadata

+

Metadata of Blog Post

- Workflow:
- {{ workflow_metadata(article, 'title') }} + Workflow:
+ {{ workflow_metadata(blog_post, 'title') }}

Current place(s)

    - {% for place in workflow_marked_places(article) %} + {% for place in workflow_marked_places(blog_post) %}
  • {{ place }}: - {{ workflow_metadata(article, 'max_num_of_words', place) ?: 'Unlimited'}} + {{ workflow_metadata(blog_post, 'max_num_of_words', place) ?: 'Unlimited'}}
  • {% endfor %}
@@ -699,10 +738,10 @@ In Twig templates, metadata is available via the ``workflow_metadata()`` functio

Enabled transition(s)

    - {% for transition in workflow_transitions(article) %} + {% for transition in workflow_transitions(blog_post) %}
  • {{ transition.name }}: - {{ workflow_metadata(article, 'priority', transition) ?: '0' }} + {{ workflow_metadata(blog_post, 'priority', transition) ?: '0' }}
  • {% endfor %}
diff --git a/workflow/dumping-workflows.rst b/workflow/dumping-workflows.rst index fa4e295eb66..aa1b78d6867 100644 --- a/workflow/dumping-workflows.rst +++ b/workflow/dumping-workflows.rst @@ -5,8 +5,8 @@ How to Dump Workflows ===================== To help you debug your workflows, you can dump a representation of your workflow -or state machine with the use of a ``DumperInterface``. Symfony provides 2 -different dumpers both based on Dot. +or state machine with the use of a ``DumperInterface``. Symfony provides two +different dumpers, both based on Dot (see below). Use the ``GraphvizDumper`` or ``StateMachineGraphvizDumper`` to create DOT files, or use ``PlantUmlDumper`` for PlantUML files. Both types can be converted @@ -24,17 +24,20 @@ Images of the workflow defined above:: .. code-block:: terminal + # dump DOT file in PNG image: $ php dump-graph-dot.php | dot -Tpng -o dot_graph.png - $ php dump-graph-puml.php | java -jar plantuml.jar -p > puml_graph.png - # run this command if you prefer SVG images: + # dump DOT file in SVG image: # $ php dump-graph-dot.php | dot -Tsvg -o dot_graph.svg + # dump PlantUML in PNG image: + $ php dump-graph-puml.php | java -jar plantuml.jar -p > puml_graph.png + The DOT result will look like this: .. image:: /_images/components/workflow/blogpost.png -The PUML result: +The PlantUML result: .. image:: /_images/components/workflow/blogpost_puml.png @@ -43,8 +46,9 @@ Inside a Symfony application, you can dump the files with those commands using .. 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 + $ php bin/console workflow:dump workflow_name | dot -Tpng -o workflow_name.png + $ php bin/console workflow:dump workflow_name | dot -Tsvg -o workflow_name.svg + $ php bin/console workflow:dump workflow_name --dump-format=puml | java -jar plantuml.jar -p > workflow_name.png .. note:: diff --git a/workflow/introduction.rst b/workflow/introduction.rst index 38a148c5fd3..b5046da83e0 100644 --- a/workflow/introduction.rst +++ b/workflow/introduction.rst @@ -58,11 +58,11 @@ your model. The most important differences between them are: Example ~~~~~~~ -A pull request starts in an initial "start" state, a state for e.g. running -tests on Travis. When this is finished, the pull request is in the "review" +A pull request starts in an initial "start" state, then a state "test" for e.g. running +tests on continuous integration stack. When this is finished, the pull request is in the "review" state, where contributors can require changes, reject or accept the pull request. At any time, you can also "update" the pull request, which -will result in another Travis run. +will result in another continuous integration run. .. image:: /_images/components/workflow/pull_request.png @@ -83,19 +83,19 @@ Below is the configuration for the pull request state machine. places: - start - coding - - travis + - test - review - merged - closed transitions: submit: from: start - to: travis + to: test update: - from: [coding, travis, review] - to: travis + from: [coding, test, review] + to: test wait_for_review: - from: travis + from: test to: review request_change: from: review @@ -129,7 +129,7 @@ Below is the configuration for the pull request state machine. start coding - travis + test review merged closed @@ -137,19 +137,19 @@ Below is the configuration for the pull request state machine. start - travis + test coding - travis + test review - travis + test - travis + test review @@ -195,7 +195,7 @@ Below is the configuration for the pull request state machine. 'places' => [ 'start', 'coding', - 'travis', + 'test', 'review', 'merged', 'closed', @@ -203,14 +203,14 @@ Below is the configuration for the pull request state machine. 'transitions' => [ 'submit'=> [ 'from' => 'start', - 'to' => 'travis', + 'to' => 'test', ], 'update'=> [ - 'from' => ['coding','travis','review'], - 'to' => 'travis', + 'from' => ['coding', 'test', 'review'], + 'to' => 'test', ], 'wait_for_review'=> [ - 'from' => 'travis', + 'from' => 'test', 'to' => 'review', ], 'request_change'=> [ pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy