Компонент Workflow

Дата обновления перевода 2024-10-12

Компонент Workflow

Компонент Workflow предоставляет инструменты для управления рабочим процессом или конечной машиной.

Установка

1
$ composer require symfony/workflow

Note

Если вы устанавливаете этот компонент вне приложения Symfony, вам нужно подключить файл vendor/autoload.php в вашем коде для включения механизма автозагрузки классов, предоставляемых Composer. Детальнее читайте в этой статье.

Создание рабочего процесса

Компонент Workflow предоставляет вам объектно-ориентированный способ для определения процесса или жизненного цикла, через который проходит ваш объект. Каждый шаг или этап в процессе называется местом. Вы также определяете переходы, которые описывают действие для перемещения из одного места в другое.

Пример диаграммы состояний для рабочего процесса с указанием переходов и мест.

Набор мест и переходов создаёт определение. Рабочему процессу нужно Definition и способ написания состояний в объекты (т.е. экземпляр MarkingStoreInterface).

Рассмотрите следующий пример для поста блога. Пост может иметь один из множества предопределённых статусов (draft, review, rejected, published). В рабочем процессе эти статусы называются местами. Вы можете определять рабочий процесс так:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use Symfony\Component\Workflow\DefinitionBuilder;
use Symfony\Component\Workflow\MarkingStore\MethodMarkingStore;
use Symfony\Component\Workflow\Transition;
use Symfony\Component\Workflow\Workflow;

$definitionBuilder = new DefinitionBuilder();
$definition = $definitionBuilder->addPlaces(['draft', 'reviewed', 'rejected', 'published'])
    // Переходы определяются с помощью уникального имени, исходного места и места назначения
    ->addTransition(new Transition('to_review', 'draft', 'reviewed'))
    ->addTransition(new Transition('publish', 'reviewed', 'published'))
    ->addTransition(new Transition('reject', 'reviewed', 'rejected'))
    ->build()
;

$singleState = true; // true если субъект может быть только в одном состоянии в заданное время
$property = 'currentState'; // имя свойства субъекта, где хранится состояние
$marking = new MethodMarkingStore($singleState, $property);
$workflow = new Workflow($definition, $marking);

Workflow теперь может помочь вам решить, какие действия разрешены в посте блога, в зависимости от того, в каком месте он находится. Он будет держать вашу логику домена в одном месте, а не распораживать её по всему вашему приложению.

Использование

Вот пример использования рабочего процесса, определённого выше:

1
2
3
4
5
6
7
8
9
10
11
// ...
// Считайте, что $blogPost находится в месте "draft" по умолчанию
$blogPost = new BlogPost();

$workflow->can($blogPost, 'publish'); // False
$workflow->can($blogPost, 'to_review'); // True

$workflow->apply($blogPost, 'to_review'); // $blogPost is now in place "reviewed"

$workflow->can($blogPost, 'publish'); // True
$workflow->getEnabledTransitions($blogPost); // $blogPost can perform transition "publish" or "reject"

Инициализация

Если свойство вашего объекта - null и вы хотите установить его с помощью initial_marking из конфигурации, вы можете вызвать метод getMarking(), чтобы инициализировать свойство объекта:

1
2
3
4
5
6
// ...
$blogPost = new BlogPost();
$workflow = $registry->get($blogPost);

// initiate workflow
$workflow->getMarking($blogPost);

Использование реестра Workflow

При определении нескольких рабочих процессов вы можете использовать Registry,
который представляет собой объект, хранящий и предоставляющий доступ к различным рабочим процессам. Реестр также поможет вам определить, поддерживает ли рабочий процесс объект, с которым вы пытаетесь его использовать:

1
2
3
4
5
6
7
8
9
10
11
use Acme\Entity\BlogPost;
use Acme\Entity\Newsletter;
use Symfony\Component\Workflow\Registry;
use Symfony\Component\Workflow\SupportStrategy\InstanceOfSupportStrategy;

$blogPostWorkflow = ...;
$newsletterWorkflow = ...;

$registry = new Registry();
$registry->addWorkflow($blogPostWorkflow, new InstanceOfSupportStrategy(BlogPost::class));
$registry->addWorkflow($newsletterWorkflow, new InstanceOfSupportStrategy(Newsletter::class));

Затем вы можете использовать регистр, чтобы получать рабочий процесс для конкретного объекта:

1
2
3
4
5
$blogPost = new BlogPost();
$workflow = $registry->get($blogPost);

// инициировать рабочий процесс
$workflow->getMarking($blogPost);

Caution

Помните, что внедрять Registry в ваши сервисы не рекомендуется.
Действительно, это препятствует некоторым оптимизациям, таким как ленивая загрузка, и может привести к снижению производительности. Вместо этого всегда следует внедрять нужный вам рабочий процесс.