Skip to content
  • About
    • What is Symfony?
    • Community
    • News
    • Contributing
    • Support
  • Documentation
    • Symfony Docs
    • Symfony Book
    • Screencasts
    • Symfony Bundles
    • Symfony Cloud
    • Training
  • Services
    • Platform.sh for Symfony Best platform to deploy Symfony apps
    • SymfonyInsight Automatic quality checks for your apps
    • Symfony Certification Prove your knowledge and boost your career
    • SensioLabs Professional services to help you with Symfony
    • Blackfire Profile and monitor performance of your apps
  • Other
  • Blog
  • Download
sponsored by
  1. Home
  2. Documentation
  3. Symfony: The Fast Track
  4. Japanese
  5. ワークフローを使って判定する

ワークフローを使って判定する

モデルが状態を持つことはよくあります。今はコメントの状態はスパムチェッカーからしか変わりませんが、他の状態の追加を検討してみましょう。

スパムチェックの後に、Webサイトの管理者が全てのコメントをモデレートしたいとしましょう。このプロセスは次の行のようなものです:

  • ユーザーがコメントを追加した際の submitted 状態から始めましょう;
  • スパムチェッカーにコメントを分析させ、 potential_spam, ham (スパムでないメール), rejected のいずれかの状態にスイッチさせるようにしましょう;
  • リジェクトされなければ、Webサイトの管理者がコメントを published もしくは rejected の状態に変更するのを待ちましょう。

ロジックを実装するのはそれほど複雑ではありませんが、さらにルールを追加することで複雑になることもあります。ロジックを自分でコーディングするのではなく、Symfony ワークフローコンポーネントを使用してみます:

1
$ symfony composer req workflow

ワークフローを記述する

コメントワークフローは、config/packages/workflow.yaml ファイルに記述されます:

config/packages/workflow.yaml
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
37
38
39
40
41
framework:
    workflows:
        comment:
            type: state_machine
            audit_trail:
                enabled: "%kernel.debug%"
            marking_store:
                type: 'method'
                property: 'state'
            supports:
                - App\Entity\Comment
            initial_marking: submitted
            places:
                - submitted
                - ham
                - potential_spam
                - spam
                - rejected
                - published
            transitions:
                accept:
                    from: submitted
                    to:   ham
                might_be_spam:
                    from: submitted
                    to:   potential_spam
                reject_spam:
                    from: submitted
                    to:   spam
                publish:
                    from: potential_spam
                    to:   published
                reject:
                    from: potential_spam
                    to:   rejected
                publish_ham:
                    from: ham
                    to:   published
                reject_ham:
                    from: ham
                    to:   rejected

ワークフローをバリデートするために、視覚的な表現を生成します:

1
$ symfony console workflow:dump comment | dot -Tpng -o workflow.png

Note

dot コマンドは、 Graphviz ユーティリティの一部です。

ワークフローを使用する

現在のメッセージハンドラーのロジックをワークフローに置き換えます:

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
37
38
39
40
41
42
43
44
45
46
47
48
--- a/src/MessageHandler/CommentMessageHandler.php
+++ b/src/MessageHandler/CommentMessageHandler.php
@@ -6,7 +6,10 @@ use App\Message\CommentMessage;
 use App\Repository\CommentRepository;
 use App\SpamChecker;
 use Doctrine\ORM\EntityManagerInterface;
+use Psr\Log\LoggerInterface;
 use Symfony\Component\Messenger\Attribute\AsMessageHandler;
+use Symfony\Component\Messenger\MessageBusInterface;
+use Symfony\Component\Workflow\WorkflowInterface;

 #[AsMessageHandler]
 class CommentMessageHandler
@@ -15,6 +18,9 @@ class CommentMessageHandler
         private EntityManagerInterface $entityManager,
         private SpamChecker $spamChecker,
         private CommentRepository $commentRepository,
+        private MessageBusInterface $bus,
+        private WorkflowInterface $commentStateMachine,
+        private ?LoggerInterface $logger = null,
     ) {
     }

@@ -25,12 +31,18 @@ class CommentMessageHandler
             return;
         }

-        if (2 === $this->spamChecker->getSpamScore($comment, $message->getContext())) {
-            $comment->setState('spam');
-        } else {
-            $comment->setState('published');
+        if ($this->commentStateMachine->can($comment, 'accept')) {
+            $score = $this->spamChecker->getSpamScore($comment, $message->getContext());
+            $transition = match ($score) {
+                2 => 'reject_spam',
+                1 => 'might_be_spam',
+                default => 'accept',
+            };
+            $this->commentStateMachine->apply($comment, $transition);
+            $this->entityManager->flush();
+            $this->bus->dispatch($message);
+        } elseif ($this->logger) {
+            $this->logger->debug('Dropping comment message', ['comment' => $comment->getId(), 'state' => $comment->getState()]);
         }
-
-        $this->entityManager->flush();
     }
 }

新しいロジックは以下のようになります:

  • メッセージ内のコメントにおいて、accept 遷移が可能であれば、スパムチェックを行います;
  • 結果に応じた正しい状態遷移を選んでください;
  • setState() メソッドを介して、コメントを更新するための apply() を呼んでください;
  • flush() を呼び、データベースに変更をコミットしてください;
  • ワークフローの再遷移を許容させるため、メッセージを再ディスパッチしてください。

管理者のバリデーションはまだ実装していないので、メッセージの取得実行をすると "コメントメッセージを削除します" とログが吐かれます。

次の章までに自動バリデーションを実装しましょう:

1
2
3
4
5
6
7
8
9
10
11
12
--- a/src/MessageHandler/CommentMessageHandler.php
+++ b/src/MessageHandler/CommentMessageHandler.php
@@ -41,6 +41,9 @@ class CommentMessageHandler
             $this->commentStateMachine->apply($comment, $transition);
             $this->entityManager->flush();
             $this->bus->dispatch($message);
+        } elseif ($this->commentStateMachine->can($comment, 'publish') || $this->commentStateMachine->can($comment, 'publish_ham')) {
+            $this->commentStateMachine->apply($comment, $this->commentStateMachine->can($comment, 'publish') ? 'publish' : 'publish_ham');
+            $this->entityManager->flush();
         } elseif ($this->logger) {
             $this->logger->debug('Dropping comment message', ['comment' => $comment->getId(), 'state' => $comment->getState()]);
         }

symfony server:log を実行し、フロントエンドでコメントが追加し、順々に状態が遷移することを確認してください。

DIコンテナからサービスを検索する

DIを使う際、インターフェースや場合によっては具体的な実装クラスの名前をタイプヒンティングとしてDIコンテナからサービスを取得しますが、インターフェースが複数の実装クラスを持っている場合、Symfonyはどのクラスが必要か推測することができません。その場合は明示する方法が必要です。

このような例は、前章で WorkflowInterface を注入した時に出くわしたばかりです。

一般的な WorkflowInterface インターフェースをコンストラクタで注入すると、Symfonyはどのようにしてどちらのワークフローの実装を使うかを推測するでしょうか? Symfonyは引数名を基にした規約を利用します: $commentStateMachine は comment ワークフロー ( state_machine 型) の設定を参照します。

もし規約が思い出せない場合は、 debug:container コマンドを使いましょう。 workflow を含む全てのサービスを検索します:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ symfony console debug:container workflow

 Select one of the following services to display its information:
  [0] console.command.workflow_dump
  [1] workflow.abstract
  [2] workflow.marking_store.method
  [3] workflow.registry
  [4] workflow.security.expression_language
  [5] workflow.twig_extension
  [6] monolog.logger.workflow
  [7] Symfony\Component\Workflow\Registry
  [8] Symfony\Component\Workflow\WorkflowInterface $commentStateMachine
  [9] Psr\Log\LoggerInterface $workflowLogger
 >

8 , Symfony\Component\Workflow\WorkflowInterface $commentStateMachine は、 $commentStateMachine を引数名として使うことに特別な意味があることを伝えています。

Note

前章で見たように debug:autowiring を使うことができます:

1
$ symfony console debug:autowiring workflow

より深く学ぶために

  • ワークフローとステートマシーン に関しての説明と、どちらを使うかについて;
  • Symfony ワークフローのドキュメント.
Previous page 非同期にする
Next page 管理者へメールを送信する
This work, including the code samples, is licensed under a Creative Commons BY-NC-SA 4.0 license.
TOC
    Version

    Symfony 6.4 is backed by

    Online Symfony certification, take it now!

    Online Symfony certification, take it now!

    Be safe against critical risks to your projects and businesses

    Be safe against critical risks to your projects and businesses

    Version:
    Locale:
    ebook

    This book is backed by:

    see all backers

    Symfony footer

    Avatar of Markus Baumer, a Symfony contributor

    Thanks Markus Baumer for being a Symfony contributor

    1 commit • 22 lines changed

    View all contributors that help us make Symfony

    Become a Symfony contributor

    Be an active part of the community and contribute ideas, code and bug fixes. Both experts and newcomers are welcome.

    Learn how to contribute

    Symfony™ is a trademark of Symfony SAS. All rights reserved.

    • What is Symfony?

      • What is Symfony?
      • Symfony at a Glance
      • Symfony Components
      • Symfony Releases
      • Security Policy
      • Logo & Screenshots
      • Trademark & Licenses
      • symfony1 Legacy
    • Learn Symfony

      • Symfony Docs
      • Symfony Book
      • Reference
      • Bundles
      • Best Practices
      • Training
      • eLearning Platform
      • Certification
    • Screencasts

      • Learn Symfony
      • Learn PHP
      • Learn JavaScript
      • Learn Drupal
      • Learn RESTful APIs
    • Community

      • Symfony Community
      • SymfonyConnect
      • Events & Meetups
      • Projects using Symfony
      • Contributors
      • Symfony Jobs
      • Backers
      • Code of Conduct
      • Downloads Stats
      • Support
    • Blog

      • All Blog Posts
      • A Week of Symfony
      • Case Studies
      • Cloud
      • Community
      • Conferences
      • Diversity
      • Living on the edge
      • Releases
      • Security Advisories
      • Symfony Insight
      • Twig
      • SensioLabs Blog
    • Services

      • SensioLabs services
      • Train developers
      • Manage your project quality
      • Improve your project performance
      • Host Symfony projects

      Powered by

    Follow Symfony