From d86dc6d4c83c7be6f97a341e291052378ed7cbb4 Mon Sep 17 00:00:00 2001 From: Antoine M Date: Fri, 12 Apr 2019 16:40:02 +0200 Subject: [PATCH 1/2] Workflow Guard documentation --- workflow/usage.rst | 80 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 61 insertions(+), 19 deletions(-) diff --git a/workflow/usage.rst b/workflow/usage.rst index e1a106e6e82..3d5187974c4 100644 --- a/workflow/usage.rst +++ b/workflow/usage.rst @@ -28,7 +28,7 @@ a ``Definition`` and a way to write the states to the objects (i.e. an instance of a :class:`Symfony\\Component\\Workflow\\MarkingStore\\MarkingStoreInterface`.) Consider the following example for a blog post that can have these places: -``draft``, ``review``, ``rejected``, ``published``. You can define the workflow +``draft``, ``reviewed``, ``rejected``, ``published``. You can define the workflow like this: .. configuration-block:: @@ -51,18 +51,18 @@ like this: initial_place: draft places: - draft - - review + - reviewed - rejected - published transitions: to_review: from: draft - to: review + to: reviewed publish: - from: review + from: reviewed to: published reject: - from: review + from: reviewed to: rejected .. code-block:: xml @@ -87,24 +87,24 @@ like this: App\Entity\BlogPost draft - review + reviewed rejected published draft - review + reviewed - review + reviewed published - review + reviewed rejected @@ -132,21 +132,21 @@ like this: 'supports' => ['App\Entity\BlogPost'], 'places' => [ 'draft', - 'review', + 'reviewed', 'rejected', 'published', ], 'transitions' => [ 'to_review' => [ 'from' => 'draft', - 'to' => 'review', + 'to' => 'reviewed', ], 'publish' => [ - 'from' => 'review', + 'from' => 'reviewed', 'to' => 'published', ], 'reject' => [ - 'from' => 'review', + 'from' => 'reviewed', 'to' => 'rejected', ], ], @@ -327,7 +327,7 @@ When a state transition is initiated, the events are dispatched in the following order: ``workflow.guard`` - Validate whether the transition is allowed at all (:ref:`see below `). + Validate whether the transition is blocked or not (:ref:`see below ` and :ref:`using guards `). The three events being dispatched are: @@ -439,14 +439,14 @@ Guard Events There are a special kind of events called "Guard events". Their event listeners are invoked every time a call to ``Workflow::can``, ``Workflow::apply`` or ``Workflow::getEnabledTransitions`` is executed. With the guard events you may -add custom logic to decide what transitions are valid or not. Here is a list +add custom logic to decide what transitions should be blocked or not. Here is a list of the guard event names. * ``workflow.guard`` * ``workflow.[workflow name].guard`` * ``workflow.[workflow name].guard.[transition name]`` -See example to make sure no blog post without title is moved to "review":: +See example to make sure no blog post without title is moved to "reviewed":: use Symfony\Component\Workflow\Event\GuardEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -460,8 +460,8 @@ See example to make sure no blog post without title is moved to "review":: $title = $post->title; if (empty($title)) { - // Posts without title are not allowed - // to perform the transition "to_review" + // Block the transition "to_review" + // if the post has no title defined $event->setBlocked(true); } } @@ -501,6 +501,48 @@ This class has two more methods: :method:`Symfony\\Component\\Workflow\\Event\\GuardEvent::setBlocked` Sets the blocked value. +.. _workflow-usage-using-guards: + +Using Guards +------------ + +The component has a guard logic to control the execution of your workflow on top of your configuration. + +It allows you to execute your custom logic to decide if the transition is blocked or not, before actually +applying this transition. + +You have multiple optional ways to use guards in your workflow. + +The first way is :ref:`with the guard event `, which allows you to implement +any desired feature. + +Another one is via the configuration and its specific entry `guard` on a transition. + +This `guard` entry allows any expression that is valid for the Expression Language component: + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/workflow.yaml + framework: + workflows: + blog_publishing: + # previous configuration + transitions: + to_review: + guard: "is_granted('ROLE_REVIEWER')" # the transition is allowed only if the current user has the ROLE_REVIEWER role. + from: draft + to: reviewed + publish: + guard: "is_authenticated" # or "is_anonymous", "is_remember_me", "is_fully_authenticated", "is_granted" + from: reviewed + to: published + reject: + guard: "has_role("ROLE_ADMIN") and subject.isStatusReviewed()" # or any valid expression language with "subject" refering to the post + from: reviewed + to: rejected + Usage in Twig ------------- @@ -542,7 +584,7 @@ The following example shows these functions in action: {% endfor %} {# Check if the object is in some specific place #} - {% if workflow_has_marked_place(post, 'review') %} + {% if workflow_has_marked_place(post, 'reviewed') %}

This post is ready for review.

{% endif %} From 96ef05a00d4e36663ef32c13e441e5bccb8573fb Mon Sep 17 00:00:00 2001 From: Antoine M Date: Mon, 15 Apr 2019 07:20:24 +0200 Subject: [PATCH 2/2] Review --- workflow/usage.rst | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/workflow/usage.rst b/workflow/usage.rst index 3d5187974c4..7b5fc28b90b 100644 --- a/workflow/usage.rst +++ b/workflow/usage.rst @@ -439,14 +439,14 @@ Guard Events There are a special kind of events called "Guard events". Their event listeners are invoked every time a call to ``Workflow::can``, ``Workflow::apply`` or ``Workflow::getEnabledTransitions`` is executed. With the guard events you may -add custom logic to decide what transitions should be blocked or not. Here is a list +add custom logic to decide which transitions should be blocked or not. Here is a list of the guard event names. * ``workflow.guard`` * ``workflow.[workflow name].guard`` * ``workflow.[workflow name].guard.[transition name]`` -See example to make sure no blog post without title is moved to "reviewed":: +This example stops any blog post being transitioned to "reviewed" if it is missing a title:: use Symfony\Component\Workflow\Event\GuardEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -461,7 +461,7 @@ See example to make sure no blog post without title is moved to "reviewed":: if (empty($title)) { // Block the transition "to_review" - // if the post has no title defined + // if the post has no title $event->setBlocked(true); } } @@ -516,9 +516,9 @@ You have multiple optional ways to use guards in your workflow. The first way is :ref:`with the guard event `, which allows you to implement any desired feature. -Another one is via the configuration and its specific entry `guard` on a transition. +Another one is via the configuration and its specific entry ``guard`` on a transition. -This `guard` entry allows any expression that is valid for the Expression Language component: +This ``guard`` entry allows any expression that is valid for the Expression Language component: .. configuration-block:: @@ -531,15 +531,18 @@ This `guard` entry allows any expression that is valid for the Expression Langua # previous configuration transitions: to_review: - guard: "is_granted('ROLE_REVIEWER')" # the transition is allowed only if the current user has the ROLE_REVIEWER role. + # the transition is allowed only if the current user has the ROLE_REVIEWER role. + guard: "is_granted('ROLE_REVIEWER')" from: draft to: reviewed publish: - guard: "is_authenticated" # or "is_anonymous", "is_remember_me", "is_fully_authenticated", "is_granted" + # or "is_anonymous", "is_remember_me", "is_fully_authenticated", "is_granted" + guard: "is_authenticated" from: reviewed to: published reject: - guard: "has_role("ROLE_ADMIN") and subject.isStatusReviewed()" # or any valid expression language with "subject" refering to the post + # or any valid expression language with "subject" refering to the post + guard: "has_role("ROLE_ADMIN") and subject.isStatusReviewed()" from: reviewed to: rejected