LoginSignup
1
1

More than 1 year has passed since last update.

オブジェクトにステータスを、"Workflow"

Last updated at Posted at 2022-12-19

Symfony Component Advent Calendar 2022の20日目の記事です。

最初に

SymfonyはPHPのフレームワークのひとつです。しかし、公式サイトの説明文には

Symfony is a set of PHP Components, a Web Application framework, a Philosophy, and a Community — all working together in harmony.
(SymfonyはPHPコンポーネントのセットで、Webアプリケーションフレームワークで、哲学、そしてコミュニティです。それらがハーモニーを奏でながら動作しています。)

と書かれている通り、PHPコンポーネントのセットで、たくさんのコンポーネントを提供しており、それらを組み合わせてひとつのフレームワークとして動作しています。Symfonyのコンポーネントは、Symfony上だけで動作するのではなく、他のPHPフレームワークやアプリケーションでも動作している強力なものが揃っています。

今回はそれらの中から、役立ちそうなもの・お薦めしたいものを紹介していきたいと思います。

※記事内ではautoloadのインポートは省略します。

オブジェクトにステータスを、"Workflow"

Workflowは、オブジェクトにステータスを持ち、次のステータスへの遷移を制御したり、確認したりするコンポーネントです。Symfony以外でも動作します。

インストール

composer require symfony/workflow

Workflowの概要

そもそもこのWorkflowですが、ブログ記事作成を業務委託した場合を想像してもらうとなんとなくわかります。
おそらく、ブログ記事を代行して書く場合、記事には

  • 下書き
  • レビュー済み
  • 公開
  • 却下

というステータスがあります。このようなステータスで運用すると

といったようなフローになると思います。このようなフローの場合、下書きから公開にすすませるわけにはいきません。
そこで、このWorkflowを利用します。

Workflowを使うことで、記事オブジェクトにステータスを与え、どのステータスからどのステータスに遷移させるかを設定して、
思いがけないステータスの変更を行わないようにします。

なお、WorkflowではステータスのことをPlace、ステータスの遷移のことをTransitionと呼びます。

Workflowの設定

フローの設定にはDefinitionBuilderを使い、その設定とMarkingStoreInterfaceで定義したステータス変更方法をもとにワークフローを作成します。


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')) // 'to_review'という名前で(下書き→レビュー済み)な遷移を作成
    ->addTransition(new Transition('publish', 'reviewed', 'published')) // 'publish'という名前で(レビュー済み→公開)
    ->addTransition(new Transition('reject', 'reviewed', 'rejected')) // 'reject'という名前で(レビュー済み→却下)
    ->build()
;

$singleState = true; // ステータスを1つしか持てないようにする
$property = 'currentState'; // ステータスを保持するプロパティ名
// ステータス変更方法オブジェクト(メソッドで変更)
$marking = new MethodMarkingStore($singleState, $property);
$workflow = new Workflow($definition, $marking);

ワークフローの適用と利用

ワークフローの適用と取得Registryオブジェクトを利用します。addWorkflow()メソッドを使い、クラスとワークフローを紐づけることで適用されます。
get()メソッドでオブジェクトを渡せば、紐づいているWorkflowオブジェクトを取得できます。

ワークフローの利用はWorkflowオブジェクトを利用します。can()メソッドは指定した遷移が可能かチェックします。遷移可能な場合、apply()メソッドを呼ぶことで、指定した遷移を行いステータスを変更します。
getEnabledTransitions()メソッドでは遷移可能な遷移を全て取得します。


use App\Entity\BlogPost;
use Symfony\Component\Workflow\Registry;
use Symfony\Component\Workflow\SupportStrategy\InstanceOfSupportStrategy;


... 上記の設定処理

// ワークフローの適用
$registry = new Registry();
// BlogPostクラスに設定したワークフローを適用
$registry->addWorkflow($workflow, new InstanceOfSupportStrategy(BlogPost::class));


// ワークフローの利用
$blogPost = new BlogPost();
// 設定されたワークフローを取得
$workflow = $registry->get($blogPost);

// 'publish'に遷移可能かチェック
$workflow->can($blogPost, 'publish'); // true

// 遷移'to_review'を行い、ステータスを'reviewed'に変更
$workflow->apply($blogPost, 'to_review'); 

// 遷移可能なものを全て取得
$transitions = $workflow->getEnabledTransisions($blogPost); // ['publish', 'reject']

DBに自動保存されるわけではないので、自分で保存処理を行う必要があります。

Symfonyの場合

Symfonyの場合、まず設定はworkflow.yamlに記載できます。

config/packages/workflow.yaml
framework:
    workflows:
        blog_publishing: # 設定名
            type: 'workflow'
            audit_trail:
                enabled: true
            marking_store:
                type: 'method'
                property: 'currentPlace'
            supports:
                - App\Entity\BlogPost
            initial_marking: draft
            places:
                - draft
                - reviewed
                - rejected
                - published
            transitions:
                to_review:
                    from: draft
                    to:   reviewed
                publish:
                    from: reviewed
                    to:   published
                reject:
                    from: reviewed
                    to:   rejected

利用する場合は、yamlで設定した設定名+Workflowをキャメルケースした変数名でWorkflowInterfaceをDIするようにしておけば、オートワイヤリングで勝手に設定されたWorkflowオブジェクトをセットしてくれます。


class SomeClass
{
    public function __construct(
        private readonly WorkflowInterface $blogPublishingWorkflow // blog_publishing + Wofkflow でキャメルケースに
    )
    {
    }

    public function someMethod(BlogPost $blogPost)
    {
        $this->blogPublishingWofkflow->apply($blogPost, 'to_review');
    }
}

Twigで利用する

Twigをインストールしている場合は、Twig上で権限チェックが利用できます。

{# オブジェクトが指定の遷移ができるかチェック #}
{% if workflow_can(post, 'publish') %}
    <a href="...">公開する</a>
{% endif %}
{% if workflow_can(post, 'reject') %}
    <a href="...">却下する</a>
{% endif %}

{# オブジェクトがreviewdかチェック #}
{% if workflow_has_marked_place(post, 'reviewed') %}
    <p>レビュー済み</p>
{% endif %}

まとめ

今回はWorkflowを紹介しました。独自にステータス管理するよりも便利なコンポーネントです。
イベントなども用意されているので、極めればとても安全にステータス管理ができます。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1