跳到內容

工作流程

編輯此頁面

在 Symfony 應用程式中使用 Workflow 組件,首先需要了解一些關於工作流程和狀態機器的基本理論和概念。閱讀本文以快速概覽。

安裝

在使用 Symfony Flex 的應用程式中,在開始使用工作流程功能之前,請執行此命令安裝它

1
$ composer require symfony/workflow

設定

若要查看所有設定選項,如果您在 Symfony 專案中使用此組件,請執行此命令

1
$ php bin/console config:dump-reference framework workflows

建立工作流程

工作流程是您的物件經歷的程序或生命週期。程序中的每個步驟或階段都稱為地點。您也定義轉換,這些轉換描述從一個地點到另一個地點所需執行的動作。

An example state diagram for a workflow, showing transitions and places.

一組地點和轉換建立一個定義。工作流程需要一個 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();

也可以透過宣告具有以下屬性的事件監聽器來監聽這些事件

這些屬性的運作方式與 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>
本作品,包含程式碼範例,以 Creative Commons BY-SA 3.0 授權條款授權。
目錄
    版本