工作流程
在 Symfony 應用程式中使用 Workflow 組件,首先需要了解一些關於工作流程和狀態機器的基本理論和概念。閱讀本文以快速概覽。
設定
若要查看所有設定選項,如果您在 Symfony 專案中使用此組件,請執行此命令
1
$ php bin/console config:dump-reference framework workflows
建立工作流程
工作流程是您的物件經歷的程序或生命週期。程序中的每個步驟或階段都稱為地點。您也定義轉換,這些轉換描述從一個地點到另一個地點所需執行的動作。

一組地點和轉換建立一個定義。工作流程需要一個 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 26 27 28
# config/packages/workflow.yaml
framework:
workflows:
blog_publishing:
type: 'workflow' # or 'state_machine'
audit_trail:
enabled: true
marking_store:
type: 'method'
property: 'currentPlace'
supports:
- App\Entity\BlogPost
initial_marking: draft
places: # defining places manually is optional
- draft
- reviewed
- rejected
- published
transitions:
to_review:
from: draft
to: reviewed
publish:
from: reviewed
to: published
reject:
from: reviewed
to: rejected
提示
如果您是第一次建立工作流程,請考慮使用 workflow:dump
命令來偵錯工作流程內容。
提示
您可以在 YAML 檔案中使用 PHP 常數,透過 !php/const
表示法。例如,您可以使用 !php/const App\Entity\BlogPost::STATE_DRAFT
而不是 'draft'
或 !php/const App\Entity\BlogPost::TRANSITION_TO_REVIEW
而不是 'to_review'
。
提示
如果您的轉換定義了工作流程中使用的所有地點,您可以省略 places
選項。Symfony 將自動從轉換中提取地點。
7.1
在 Symfony 7.1 中引入了對省略 places
選項的支援。
設定的屬性將透過標記儲存實作的 getter/setter 方法來使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
// src/Entity/BlogPost.php
namespace App\Entity;
class BlogPost
{
// the configured marking store property must be declared
private string $currentPlace;
private string $title;
private string $content;
// getter/setter methods must exist for property access by the marking store
public function getCurrentPlace(): string
{
return $this->currentPlace;
}
public function setCurrentPlace(string $currentPlace, array $context = []): void
{
$this->currentPlace = $currentPlace;
}
// you don't need to set the initial marking in the constructor or any other method;
// this is configured in the workflow with the 'initial_marking' option
}
也可以使用 public 屬性作為標記儲存。上述類別將變成以下內容
1 2 3 4 5 6 7 8 9 10
// src/Entity/BlogPost.php
namespace App\Entity;
class BlogPost
{
// the configured marking store property must be declared
public string $currentPlace;
public string $title;
public string $content;
}
當使用 public 屬性時,不支援 context。為了支援它,您必須宣告一個 setter 來寫入您的屬性
1 2 3 4 5 6 7 8 9 10 11 12 13
// src/Entity/BlogPost.php
namespace App\Entity;
class BlogPost
{
public string $currentPlace;
// ...
public function setCurrentPlace(string $currentPlace, array $context = []): void
{
// assign the property and do something with the context
}
}
注意
標記儲存類型可以是 "multiple_state" 或 "single_state"。單一狀態標記儲存不支援模型同時處於多個地點。這表示 "workflow" 必須使用 "multiple_state" 標記儲存,而 "state_machine" 必須使用 "single_state" 標記儲存。Symfony 預設會根據 "type" 設定標記儲存,因此最好不要設定它。
單一狀態標記儲存使用 string
來儲存資料。多重狀態標記儲存使用 array
來儲存資料。如果未定義狀態標記儲存,您必須在這兩種情況下都回傳 null
(例如,上述範例應定義像 App\Entity\BlogPost::getCurrentPlace(): ?array
或像 App\Entity\BlogPost::getCurrentPlace(): ?string
的回傳類型)。
提示
marking_store.type
(預設值取決於 type
值)和 property
(預設值 ['marking']
)屬性是 marking_store
選項的可選項目。如果省略,將使用其預設值。強烈建議使用預設值。
提示
將 audit_trail.enabled
選項設定為 true
會讓應用程式為工作流程活動產生詳細的記錄訊息。
使用這個名為 blog_publishing
的工作流程,您可以獲得協助來決定部落格文章上允許哪些動作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
use App\Entity\BlogPost;
use Symfony\Component\Workflow\Exception\LogicException;
$post = new BlogPost();
// you don't need to set the initial marking with code; this is configured
// in the workflow with the 'initial_marking' option
$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 $exception) {
// ...
}
// See all the available transitions for the post in the current state
$transitions = $workflow->getEnabledTransitions($post);
// See a specific available transition for the post in the current state
$transition = $workflow->getEnabledTransition($post, 'publish');
使用多重狀態標記儲存
如果您正在建立工作流程,您的標記儲存可能需要同時包含多個地點。這就是為什麼如果您使用 Doctrine,相符的資料行定義應使用 json
類型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
// src/Entity/BlogPost.php
namespace App\Entity;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class BlogPost
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private int $id;
#[ORM\Column(type: Types::JSON)]
private array $currentPlaces;
// ...
}
警告
您不應將 simple_array
類型用於您的標記儲存。在多重狀態標記儲存中,地點會儲存為鍵,值為一,例如 ['draft' => 1]
。如果標記儲存僅包含一個地點,則此 Doctrine 類型只會將其值儲存為字串,導致物件目前地點遺失。
在類別中存取工作流程
您可以透過使用 服務自動裝配 和使用 camelCased 工作流程名稱 + Workflow
作為參數名稱,在類別中使用工作流程。如果是狀態機器類型,請使用 camelCased 工作流程名稱 + StateMachine
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
use App\Entity\BlogPost;
use Symfony\Component\Workflow\WorkflowInterface;
class MyClass
{
public function __construct(
// Symfony will inject the 'blog_publishing' workflow configured before
private WorkflowInterface $blogPublishingWorkflow,
) {
}
public function toReview(BlogPost $post): void
{
// Update the currentState on the post
try {
$this->blogPublishingWorkflow->apply($post, 'to_review');
} catch (LogicException $exception) {
// ...
}
// ...
}
}
若要取得工作流程的已啟用轉換,您可以使用 getEnabledTransition() 方法。
7.1
getEnabledTransition() 方法在 Symfony 7.1 中引入。
工作流程也可以透過其名稱和 Target 屬性來注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14
use App\Entity\BlogPost;
use Symfony\Component\DependencyInjection\Attribute\Target;
use Symfony\Component\Workflow\WorkflowInterface;
class MyClass
{
public function __construct(
#[Target('blog_publishing')]
private WorkflowInterface $workflow
) {
}
// ...
}
這可讓您將任何實作名稱的引數名稱解耦。
提示
如果您想檢索所有工作流程,例如為了文件目的,您可以注入所有服務,使用以下標籤
workflow
:所有工作流程和所有狀態機器;workflow.workflow
:所有工作流程;workflow.state_machine
:所有狀態機器。
請注意,工作流程元數據附加在 metadata
鍵下的標籤中,讓您更瞭解工作流程的上下文和資訊。深入瞭解標籤屬性和儲存工作流程元數據。
7.1
附加到標籤的設定是在 Symfony 7.1 中引入的。
提示
您可以使用 php bin/console debug:autowiring workflow
命令找到可用工作流程服務的清單。
使用事件
為了讓您的工作流程更靈活,您可以使用 EventDispatcher
建構 Workflow
物件。您現在可以建立事件監聽器來封鎖轉換(即取決於部落格文章中的資料),並在工作流程操作發生時執行額外動作(例如,傳送公告)。
每個步驟都有三個依序觸發的事件
- 每個工作流程的事件;
- 相關工作流程的事件;
- 針對具有特定轉換或地點名稱的相關工作流程的事件。
當狀態轉換啟動時,事件會依以下順序派發
workflow.guard
-
正在派發的三個事件是
workflow.guard
workflow.[工作流程名稱].guard
workflow.[工作流程名稱].guard.[轉換名稱]
workflow.leave
-
主體即將離開一個地點。
正在派發的三個事件是
workflow.leave
workflow.[工作流程名稱].leave
workflow.[工作流程名稱].leave.[地點名稱]
workflow.transition
-
主體正在經歷此轉換。
正在派發的三個事件是
workflow.transition
workflow.[工作流程名稱].transition
workflow.[工作流程名稱].transition.[轉換名稱]
workflow.enter
-
主體即將進入一個新地點。此事件在更新主體地點之前立即觸發,這表示主體的標記尚未以新地點更新。
正在派發的三個事件是
workflow.enter
workflow.[工作流程名稱].enter
workflow.[工作流程名稱].enter.[地點名稱]
workflow.entered
-
主體已進入地點,且標記已更新。
正在派發的三個事件是
workflow.entered
workflow.[工作流程名稱].entered
workflow.[工作流程名稱].entered.[地點名稱]
workflow.completed
-
物件已完成此轉換。
正在派發的三個事件是
workflow.completed
workflow.[工作流程名稱].completed
workflow.[工作流程名稱].completed.[轉換名稱]
workflow.announce
-
針對現在可供主體存取的每個轉換觸發。
正在派發的三個事件是
workflow.announce
workflow.[工作流程名稱].announce
workflow.[工作流程名稱].announce.[轉換名稱]
套用轉換後,announce 事件會測試所有可用的轉換。這將再次觸發所有守衛事件,如果它們包含密集的 CPU 或資料庫工作負載,可能會影響效能。
如果您不需要 announce 事件,請使用 context 停用它
1
$workflow->apply($subject, $transitionName, [Workflow::DISABLE_ANNOUNCE_EVENT => true]);
注意
即使對於停留在同一地點的轉換,也會觸發離開和進入事件。
注意
如果您透過呼叫 $workflow->getMarking($object);
初始化標記,則 workflow.[workflow_name].entered.[initial_place_name]
事件將以預設 context (Workflow::DEFAULT_INITIAL_CONTEXT
) 呼叫。
以下是如何為每次 "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 30 31 32 33 34 35
// src/App/EventSubscriber/WorkflowLoggerSubscriber.php
namespace App\EventSubscriber;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Workflow\Event\Event;
use Symfony\Component\Workflow\Event\LeaveEvent;
class WorkflowLoggerSubscriber implements EventSubscriberInterface
{
public function __construct(
private LoggerInterface $logger,
) {
}
public function onLeave(Event $event): void
{
$this->logger->alert(sprintf(
'Blog post (id: "%s") performed transition "%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(): array
{
return [
LeaveEvent::getName('blog_publishing') => 'onLeave',
// if you prefer, you can write the event name manually like this:
// 'workflow.blog_publishing.leave' => 'onLeave',
];
}
}
提示
所有內建工作流程事件都定義了 getName(?string $workflowName, ?string $transitionOrPlaceName)
方法,以建構完整的事件名稱,而無需處理字串。您也可以透過 EventNameTrait 在您的自訂事件中使用此方法。
7.1
getName()
方法在 Symfony 7.1 中引入。
如果某些監聽器在轉換期間更新了 context,您可以透過標記檢索它
1 2 3 4
$marking = $workflow->apply($post, 'to_review');
// contains the new value
$marking->getContext();
也可以透過宣告具有以下屬性的事件監聽器來監聽這些事件
- AsAnnounceListener
- AsCompletedListener
- AsEnterListener
- AsEnteredListener
- AsGuardListener
- AsLeaveListener
- AsTransitionListener
這些屬性的運作方式與 AsEventListener 屬性類似
1 2 3 4 5 6 7 8 9 10
class ArticleWorkflowEventListener
{
#[AsTransitionListener(workflow: 'my-workflow', transition: 'published')]
public function onPublishedTransition(TransitionEvent $event): void
{
// ...
}
// ...
}
您可以參考關於使用 PHP 屬性定義事件監聽器的文件以瞭解更多用法。
守衛事件
有稱為 "Guard events" 的特殊事件類型。每當執行 Workflow::can()
、Workflow::apply()
或 Workflow::getEnabledTransitions()
的呼叫時,都會叫用其事件監聽器。透過守衛事件,您可以新增自訂邏輯來決定應封鎖哪些轉換。
workflow.guard
workflow.[工作流程名稱].guard
workflow.[工作流程名稱].guard.[轉換名稱]
以下是守衛事件名稱的清單。
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
// src/App/EventSubscriber/BlogPostReviewSubscriber.php
namespace App\EventSubscriber;
use App\Entity\BlogPost;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Workflow\Event\GuardEvent;
class BlogPostReviewSubscriber implements EventSubscriberInterface
{
public function guardReview(GuardEvent $event): void
{
/** @var BlogPost $post */
$post = $event->getSubject();
$title = $post->title;
if (empty($title)) {
$event->setBlocked(true, 'This blog post cannot be marked as reviewed because it has no title.');
}
}
public static function getSubscribedEvents(): array
{
return [
'workflow.blog_publishing.guard.to_review' => ['guardReview'],
];
}
}
選擇要派發哪些事件
此範例會停止任何部落格文章轉換為 "reviewed",如果它缺少標題
1 2 3 4 5 6 7 8 9 10 11
# config/packages/workflow.yaml
framework:
workflows:
blog_publishing:
# you can pass one or more event names
events_to_dispatch: ['workflow.leave', 'workflow.completed']
# pass an empty array to not dispatch any event
events_to_dispatch: []
# ...
如果您偏好控制在執行每次轉換時觸發哪些事件,請使用 events_to_dispatch
設定選項。此選項不適用於守衛事件,它們始終會觸發
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
use App\Entity\BlogPost;
use Symfony\Component\Workflow\Exception\LogicException;
$post = new BlogPost();
$workflow = $this->container->get('workflow.blog_publishing');
try {
$workflow->apply($post, 'to_review', [
Workflow::DISABLE_ANNOUNCE_EVENT => true,
Workflow::DISABLE_LEAVE_EVENT => true,
]);
} catch (LogicException $exception) {
// ...
}
您也可以在套用轉換時停用要觸發的特定事件
針對特定轉換停用事件將優先於工作流程設定中指定的任何事件。在上述範例中,即使 workflow.leave
事件已指定為工作流程設定中所有轉換要派發的事件,也不會觸發。
這些是所有可用的常數
Workflow::DISABLE_LEAVE_EVENT
Workflow::DISABLE_TRANSITION_EVENT
Workflow::DISABLE_ENTER_EVENT
Workflow::DISABLE_ENTERED_EVENT
事件方法
Workflow::DISABLE_COMPLETED_EVENT
- 每個工作流程事件都是 Event 的實例。這表示每個事件都可以存取以下資訊
- getMarking()
- 傳回工作流程的 Marking。
- getSubject()
- 傳回派發事件的物件。
- getTransition()
- 傳回派發事件的 Transition。
- getWorkflowName()
- 傳回觸發事件的工作流程名稱字串。
- getMetadata()
傳回元數據。
- 對於守衛事件,有一個擴充的 GuardEvent 類別。此類別具有以下額外方法
- isBlocked()
- 傳回轉換是否被封鎖。
- setBlocked()
- 設定封鎖值。
- getTransitionBlockerList()
- 傳回事件 TransitionBlockerList。請參閱封鎖轉換。
- addTransitionBlocker()
封鎖轉換
新增 TransitionBlocker 實例。
工作流程的執行可以透過呼叫自訂邏輯來控制,以決定在套用轉換之前是否應封鎖或允許目前的轉換。此功能由 "guards" 提供,可以使用兩種方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
# config/packages/workflow.yaml
framework:
workflows:
blog_publishing:
# previous configuration
transitions:
to_review:
# the transition is allowed only if the current user has the ROLE_REVIEWER role.
guard: "is_granted('ROLE_REVIEWER')"
from: draft
to: reviewed
publish:
# 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 supported object
guard: "is_granted('ROLE_ADMIN') and subject.isRejectable()"
from: reviewed
to: rejected
首先,您可以監聽守衛事件。或者,您可以為轉換定義 guard
設定選項。此選項的值是使用 ExpressionLanguage 組件建立的任何有效運算式
這個範例已經過簡化;在正式環境中,您可能會偏好使用 Translation 元件來集中管理訊息
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 30 31
// src/App/EventSubscriber/BlogPostPublishSubscriber.php
namespace App\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Workflow\Event\GuardEvent;
use Symfony\Component\Workflow\TransitionBlocker;
class BlogPostPublishSubscriber implements EventSubscriberInterface
{
public function guardPublish(GuardEvent $event): void
{
$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(): array
{
return [
'workflow.blog_publishing.guard.publish' => ['guardPublish'],
];
}
}
建立您自己的標記儲存
您可能需要實作自己的儲存機制,以便在標記更新時執行一些額外的邏輯。例如,您可能有一些特定的需求,需要在特定的工作流程上儲存標記。若要執行此操作,您需要實作 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
namespace App\Workflow\MarkingStore;
use Symfony\Component\Workflow\Marking;
use Symfony\Component\Workflow\MarkingStore\MarkingStoreInterface;
final class BlogPostMarkingStore implements MarkingStoreInterface
{
/**
* @param BlogPost $subject
*/
public function getMarking(object $subject): Marking
{
return new Marking([$subject->getCurrentPlace() => 1]);
}
/**
* @param BlogPost $subject
*/
public function setMarking(object $subject, Marking $marking, array $context = []): void
{
$marking = key($marking->getPlaces());
$subject->setCurrentPlace($marking);
}
}
一旦您的標記儲存機制實作完成,您就可以設定您的工作流程來使用它
1 2 3 4 5 6 7
# config/packages/workflow.yaml
framework:
workflows:
blog_publishing:
# ...
marking_store:
service: 'App\Workflow\MarkingStore\BlogPostMarkingStore'
在 Twig 中使用
Symfony 定義了幾個 Twig 函式來管理工作流程,並減少在您的範本中對網域邏輯的需求
workflow_can()
- 如果給定的物件可以進行給定的轉換,則傳回
true
。 workflow_transitions()
- 傳回一個陣列,其中包含給定物件所有已啟用的轉換。
workflow_transition()
- 傳回給定物件和轉換名稱所啟用的特定轉換。
workflow_marked_places()
- 傳回一個陣列,其中包含給定標記的地點名稱。
workflow_has_marked_place()
- 如果給定物件的標記具有給定的狀態,則傳回
true
。 workflow_transition_blockers()
- 傳回給定轉換的 TransitionBlockerList。
以下範例展示了這些函式的實際應用
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 30 31 32
<h3>Actions on Blog Post</h3>
{% if workflow_can(post, 'publish') %}
<a href="...">Publish</a>
{% endif %}
{% if workflow_can(post, 'to_review') %}
<a href="...">Submit to review</a>
{% endif %}
{% if workflow_can(post, 'reject') %}
<a href="...">Reject</a>
{% endif %}
{# Or loop through the enabled transitions #}
{% for transition in workflow_transitions(post) %}
<a href="...">{{ transition.name }}</a>
{% else %}
No actions available.
{% endfor %}
{# Check if the object is in some specific place #}
{% if workflow_has_marked_place(post, 'reviewed') %}
<p>This post is ready for review.</p>
{% endif %}
{# Check if some place has been marked on the object #}
{% if 'reviewed' in workflow_marked_places(post) %}
<span class="label">Reviewed</span>
{% endif %}
{# Loop through the transition blockers #}
{% for blocker in workflow_transition_blockers(post, 'publish') %}
<span class="error">{{ blocker.message }}</span>
{% endfor %}
儲存元數據
如果您需要,您可以使用 metadata
選項在工作流程、其地點及其轉換中儲存任意的中繼資料。此中繼資料可以是工作流程的標題,或是非常複雜的物件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
# config/packages/workflow.yaml
framework:
workflows:
blog_publishing:
metadata:
title: 'Blog Publishing Workflow'
# ...
places:
draft:
metadata:
max_num_of_words: 500
# ...
transitions:
to_review:
from: draft
to: review
metadata:
priority: 0.5
publish:
from: reviewed
to: published
metadata:
hour_limit: 20
explanation: 'You can not publish after 8 PM.'
然後,您可以依照以下方式在您的控制器中存取此中繼資料
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
// src/App/Controller/BlogPostController.php
use App\Entity\BlogPost;
use Symfony\Component\Workflow\WorkflowInterface;
// ...
public function myAction(WorkflowInterface $blogPublishingWorkflow, BlogPost $post): Response
{
$title = $blogPublishingWorkflow
->getMetadataStore()
->getWorkflowMetadata()['title'] ?? 'Default title'
;
$maxNumOfWords = $blogPublishingWorkflow
->getMetadataStore()
->getPlaceMetadata('draft')['max_num_of_words'] ?? 500
;
$aTransition = $blogPublishingWorkflow->getDefinition()->getTransitions()[0];
$priority = $blogPublishingWorkflow
->getMetadataStore()
->getTransitionMetadata($aTransition)['priority'] ?? 0
;
// ...
}
有一個 getMetadata()
方法可用於所有類型的中繼資料
1 2 3 4 5 6 7 8
// get "workflow metadata" passing the metadata key as argument
$title = $workflow->getMetadataStore()->getMetadata('title');
// get "place metadata" passing the metadata key as the first argument and the place name as the second argument
$maxNumOfWords = $workflow->getMetadataStore()->getMetadata('max_num_of_words', 'draft');
// get "transition metadata" passing the metadata key as the first argument and a Transition object as the second argument
$priority = $workflow->getMetadataStore()->getMetadata('priority', $aTransition);
在您的控制器中的 快閃訊息 中
1 2 3 4 5
// $transition = ...; (an instance of Transition)
// $workflow is an injected Workflow instance
$title = $workflow->getMetadataStore()->getMetadata('title', $transition);
$this->addFlash('info', "You have successfully applied the transition with title: '$title'");
也可以從 Event 物件,在 Listener 中存取中繼資料。
在 Twig 範本中,中繼資料可以透過 workflow_metadata()
函式取得
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 30 31 32 33 34 35 36
<h2>Metadata of Blog Post</h2>
<p>
<strong>Workflow</strong>:<br>
<code>{{ workflow_metadata(blog_post, 'title') }}</code>
</p>
<p>
<strong>Current place(s)</strong>
<ul>
{% for place in workflow_marked_places(blog_post) %}
<li>
{{ place }}:
<code>{{ workflow_metadata(blog_post, 'max_num_of_words', place) ?: 'Unlimited'}}</code>
</li>
{% endfor %}
</ul>
</p>
<p>
<strong>Enabled transition(s)</strong>
<ul>
{% for transition in workflow_transitions(blog_post) %}
<li>
{{ transition.name }}:
<code>{{ workflow_metadata(blog_post, 'priority', transition) ?: 0 }}</code>
</li>
{% endfor %}
</ul>
</p>
<p>
<strong>to_review Priority</strong>
<ul>
<li>
to_review:
<code>{{ workflow_metadata(blog_post, 'priority', workflow_transition(blog_post, 'to_review')) }}</code>
</li>
</ul>
</p>