diff --git a/workflow/state-machines.rst b/workflow/state-machines.rst index 4a5952c9f1d..11d03896cd9 100644 --- a/workflow/state-machines.rst +++ b/workflow/state-machines.rst @@ -35,7 +35,7 @@ Below is the configuration for the pull request state machine. type: 'state_machine' supports: - App\Entity\PullRequest - initial_place: start + initial_marking: [start] places: - start - coding @@ -79,7 +79,7 @@ Below is the configuration for the pull request state machine. - + start App\Entity\PullRequest @@ -147,6 +147,7 @@ Below is the configuration for the pull request state machine. 'workflows' => [ 'pull_request' => [ 'type' => 'state_machine', + 'initial_marking' => ['start'] 'supports' => ['App\Entity\PullRequest'], 'places' => [ 'start', diff --git a/workflow/usage.rst b/workflow/usage.rst index c613b76dd3a..cd09df8deea 100644 --- a/workflow/usage.rst +++ b/workflow/usage.rst @@ -43,12 +43,12 @@ like this: audit_trail: enabled: true marking_store: - type: 'multiple_state' # one of 'single_state', 'multiple_state', 'method' - arguments: - - 'currentPlace' + # Method will become the default value in Symfony 5.0 + type: method + property: currentPlace supports: - App\Entity\BlogPost - initial_place: draft + initial_marking: [draft] places: - draft - review @@ -68,7 +68,7 @@ like this: .. code-block:: xml - + - - currentPlace - + draft + + App\Entity\BlogPost @@ -117,6 +117,7 @@ like this: .. code-block:: php // config/packages/workflow.php + $container->loadFromExtension('framework', [ // ... 'workflows' => [ @@ -126,10 +127,12 @@ like this: 'enabled' => true ], 'marking_store' => [ - 'type' => 'multiple_state', // one of 'single_state', 'multiple_state', 'method' - 'arguments' => ['currentPlace'], + // Method will become the default value in Symfony 5.0 + 'type' => 'method', + 'property' => 'currentPlace', ], 'supports' => ['App\Entity\BlogPost'], + 'initial_marking' => ['draft'], 'places' => [ 'draft', 'review', @@ -166,19 +169,19 @@ As configured, the following property is used by the marking store:: .. note:: - The marking store type could be "multiple_state", "single_state" or "method". - A single state marking store does not support a model being on multiple places - at the same time. + The Workflow Component supports workflows that can be in one or multiple places + (states) at the same time. - versionadded:: 4.3 + If the subject can be in only on state: use a state machine. In that case, the + property (named ``marking`` by default) will be a string. - The ``method`` marking store type was introduced in Symfony 4.3. + If the subject can be in many places: use a workflow. In that case, the property will be an array. .. tip:: - The ``type`` (default value ``single_state``) and ``arguments`` (default + The ``type`` (default value ``method``) and ``property`` (default value ``marking``) attributes of the ``marking_store`` option are optional. - If omitted, their default values will be used. + If omitted, the default values will be used. .. tip:: @@ -224,90 +227,11 @@ you can get the workflow by injecting the Workflow registry service:: // ... if the transition is not allowed } - // Update the currentState on the post passing some contextual data - // to the whole workflow process - try { - $workflow->apply($post, 'publish', [ - 'log_comment' => 'My logging comment for the publish transition.', - ]); - } 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 - - The :class:`Symfony\\Component\\Workflow\\Exception\\TransitionException` - class was introduced in Symfony 4.1. - -.. versionadded:: 4.1 - - The :method:`Symfony\\Component\\Workflow\\Registry::all` method was - introduced in Symfony 4.1. - -You can pass some context as the second argument of the ``apply()`` method. -This can be useful when the subject not only needs to apply a transition, -but for example you also want to log the context in which the switch happened. - -This context is forwarded to the :method:`Symfony\\Component\\Workflow\\MarkingStore\\MarkingStoreInterface::setMarking` -method of the marking store. - -.. versionadded:: 4.3 - - The ``$context`` argument of the :method:`Symfony\\Component\\Workflow\\Workflow::apply` - method was introduced in Symfony 4.3. - -.. tip:: - - Configure the ``type`` as ``method`` of the ``marking_store`` option to use this feature - without implementing your own marking store. - -You can also use this ``$context`` in your own marking store implementation. -A simple implementation example is when you want to store the place as integer instead of string in your object. - -Lets say your object has a status property, stored as an integer in your storage, and you want to log an optional -comment any time the status changes:: - - // your own implementation class, to define in the configuration "marking_store" - - class ObjectMarkingStore implements MarkingStoreInterface - { - public function getMarking($subject) - { - $subject->getStatus(); - // ... - // return a marking - } - - public function setMarking($subject, Marking $marking, array $context); - { - // ... - $subject->setStatus($newStatus, $context['log_comment'] ?? null); - } - } - - // and in your Object class - - public function getStatus() - { - return $this->status; - } - - public function setStatus(int $status, ?string $comment = null) - { - $this->status = $status; - $this->addStatusLogRecord(new StatusLog($this, $comment)); - - return $this; - } - - // the StatusLog class can have a createdAt, a username, - // the new status, and finally your optional comment retrieved from the workflow context. - Using Events ------------ @@ -455,7 +379,7 @@ See example to make sure no blog post without title is moved to "review":: { public function guardReview(GuardEvent $event) { - /** @var App\Entity\BlogPost $post */ + /** @var \App\Entity\BlogPost $post */ $post = $event->getSubject(); $title = $post->title; @@ -549,70 +473,3 @@ The following example shows these functions in action: {% if 'waiting_some_approval' in workflow_marked_places(post) %} PENDING {% endif %} - -Transition Blockers -------------------- - -.. versionadded:: 4.1 - - Transition Blockers were introduced in Symfony 4.1. - -Transition Blockers provide a way to return a human-readable message for why a -transition was blocked:: - - use Symfony\Component\Workflow\Event\GuardEvent; - use Symfony\Component\EventDispatcher\EventSubscriberInterface; - - class BlogPostPublishListener implements EventSubscriberInterface - { - public function guardPublish(GuardEvent $event) - { - /** @var \App\Entity\BlogPost $post */ - $post = $event->getSubject(); - - // If it's after 9pm, prevent publication - if (date('H') > 21) { - $event->addTransitionBlocker( - new TransitionBlocker( - "You can not publish this blog post because it's too late. Try again tomorrow morning." - ) - ); - } - } - - public static function getSubscribedEvents() - { - return [ - 'workflow.blogpost.guard.publish' => ['guardPublish'], - ]; - } - } - -You can access the message from a Twig template as follows: - -.. code-block:: html+twig - -

Publication was blocked because:

-
    - {% for transition in workflow_all_transitions(article) %} - {% if not workflow_can(article, transition.name) %} -
  • - {{ transition.name }}: -
      - {% for blocker in workflow_build_transition_blocker_list(article, transition.name) %} -
    • - {{ blocker.message }} - {% if blocker.parameters.expression is defined %} - {{ blocker.parameters.expression }} - {% endif %} -
    • - {% endfor %} -
    -
  • - {% endif %} - {% endfor %} -
- -Don't need a human-readable message? You can still use:: - - $event->setBlocked('true');