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
に記載できます。
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
を紹介しました。独自にステータス管理するよりも便利なコンポーネントです。
イベントなども用意されているので、極めればとても安全にステータス管理ができます。