Как использовать рабочий поток
Дата обновления перевода 2023-07-06
Как использовать рабочий поток
Рабочий поток - это процесс жизненного цикла, через который проходят ваши объекты. Каждый шаг или этап процесса называется местом. Вы также определяете переходы, которые описывают действие перехода из одного места в другое.
Набор мест и переходов создаёт определение. Рабочему потоку необходимо Definition
(определение) и способ написать состояния объектов (например, сущность класса
MarkingStoreInterface.)
Рассмотрите следующий пример для записи блога. Запись может иметь такие места: "черновик", "обзор", "отвергнутый", "опубликованный". Вы можете определить рабочий поток следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
framework:
workflows:
blog_publishing:
type: 'workflow' # or 'state_machine'
marking_store:
type: 'multiple_state' # or 'single_state'
arguments:
- 'currentPlace'
supports:
- AppBundle\Entity\BlogPost
places:
- draft
- review
- rejected
- published
transitions:
to_review:
from: draft
to: review
publish:
from: review
to: published
reject:
from: review
to: rejected
1 2 3 4 5 6 7
class BlogPost
{
// Это свойство используется хранилищем маркировки
public $currentPlace;
public $title;
public $content;
}
Note
Тип хранилища маркировки может быть множественным и единственным состоянием ("multiple_state" или "single_state"). Единственное состояние не поддерживает модель, которая нахолится в нескольких местах одновременно.
Tip
Атрибуты type
(значение по умолчанию single_state
) и arguments
(значение
по умолчанию marking
) опции marking_store
необязательны. Если их опустить,
будут использованы их значения по умолчанию.
С этим рабочим потоком, названным blog_publishing
, вы можете получить помощь в решении
того, какие действия разрешены в записи блога:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
$post = new \AppBundle\Entity\BlogPost();
$workflow = $this->container->get('workflow.blog_publishing');
$workflow->can($post, 'publish'); // False
$workflow->can($post, 'to_review'); // True
// Update the currentState on the post
try {
$workflow->apply($post, 'to_review');
} catch (LogicException $e) {
// ...
}
// Смотрите все доступные переходы для записи в текущем состоянии
$transitions = $workflow->getEnabledTransitions($post);
Использование событий
Чтобы сделать ваши рабочие потоки еще более мощными, вы можете построить объект
Workflow
с EventDispatcher
. Теперь вы можете создавать слушателей событий
для блокировки переходов (т.е. в зависимости от данных в записи блога). Развёртываются
следующие события:
workflow.leave
workflow.[workflow name].leave
workflow.[workflow name].leave.[place name]
workflow.transition
workflow.[workflow name].transition
workflow.[workflow name].transition.[transition name]
workflow.enter
workflow.[workflow name].enter
workflow.[workflow name].enter.[place name]
workflow.entered
workflow.[workflow name].entered
workflow.[workflow name].entered.[place name]
workflow.announce
workflow.[workflow name].announce
workflow.[workflow name].announce.[transition name]
Вот пример того, как включать логирование каждый раз, когда рабочий поток "blog_publishing" покидает место:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Workflow\Event\Event;
class WorkflowLogger implements EventSubscriberInterface
{
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
public function onLeave(Event $event)
{
$this->logger->alert(sprintf(
'Blog post (id: "%s") performed transaction "%s" from "%s" to "%s"',
$event->getSubject()->getId(),
$event->getTransition()->getName(),
implode(', ', array_keys($event->getMarking()->getPlaces())),
implode(', ', $event->getTransition()->getTos())
));
}
public static function getSubscribedEvents()
{
return array(
'workflow.blog_publishing.leave' => 'onLeave',
);
}
}
Защитные события
Существует особенный тип событий, под названием "защитные собтия". Их слушатели
событий вызываются каждый раз, когда выполняется вызов Workflow::can
, Workflow::apply
или Workflow::getEnabledTransitions
. С защитными собтиями вы можете добавлять пользовательскую
логику для того, чтобы решить, какие переходы валидны, а какие - нет. Вот список имён
защитных событий.
workflow.guard
workflow.[workflow name].guard
workflow.[workflow name].guard.[transition name]
Смотрите пример, чтобы убедиться, что ни одна запись блога без заголовка не будет перенесена в состояние "обзора":
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
use Symfony\Component\Workflow\Event\GuardEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class BlogPostReviewListener implements EventSubscriberInterface
{
public function guardReview(GuardEvent $event)
{
/** @var \AppBundle\Entity\BlogPost $post */
$post = $event->getSubject();
$title = $post->title;
if (empty($title)) {
// Записи без заголовок не должны быть допущены
$event->setBlocked(true);
}
}
public static function getSubscribedEvents()
{
return array(
'workflow.blogpost.guard.to_review' => array('guardReview'),
);
}
}
Методы событий
Каждое событие рабочего потока - это экземпляр Event. Это означает, что каждое событие имеет доступ к следующей информации:
- getMarking()
- Возвращает Marking рабочего потока.
- getSubject()
- Возвращает объект, который развёртывает событие.
- getTransition()
- Возвращает Transition, который развёртывает событие.
- getWorkflowName()
-
Возвращает строку с именем рабочего потока, который вызвал событие.
3.3
Метод
getWorkflowName()
был представлен в Symfony 3.3.
For Guard Events, there is an extended class GuardEvent. This class has two more methods:
- isBlocked()
- Возвращается, если переход заблокирован.
- setBlocked()
- Устанавливает заблокированное значение.
Использование в Twig
Symfony определяет несколько функций Twig для управления рабочими потоками и уменьшения необходимости логики домена в ваших шаблонах:
workflow_can()
-
Возвращает
true
, если данный объект может выполнить данный переход. workflow_transitions()
- Возвращает массив со всеми переходами, включенными в данном объекте.
workflow_marked_places()
- Возвращает массив с именами мест данной маркировки.
workflow_has_marked_place()
-
Возвращает
true
, если маркировка данного объекта имеет данное состояние. - .. versionadded:: 3.3
-
Функции
workflow_marked_places()
иworkflow_has_marked_place()
были представлены в Symfony 3.3.
Следующий пример иллюстрирует эти функции в действии:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
<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 %}
{# Или закольцевать через включенные переходы #}
{% for transition in workflow_transitions(post) %}
<a href="...">{{ transition.name }}</a>
{% else %}
No actions available.
{% endfor %}
{# Проверить, находится ли объект в некотором особенном месте #}
{% if workflow_has_marked_place(post, 'to_review') %}
<p>This post is ready for review.</p>
{% endif %}
{# Проверить, если некоторое место было маркировано в объекте #}
{% if 'waiting_some_approval' in workflow_marked_places(post) %}
<span class="label">PENDING</span>
{% endif %}