گام 19: تصمیم‌گیری با یک جریان‌کار

5.0 version
Maintained

تصمیم‌گیری با یک جریان‌کار

داشتن یک وضعیت برای یک مدل، کاملاً عادی است. وضعیت یک کامنت، تنها توسط بررسی‌کننده‌ی محتوای هرز تعیین شده است. اگر ما فاکتورهای تصمیم‌گیری بیشتری اضافه کنیم، چه اتفاقی می‌افتد؟

ما ممکن است بخواهیم که پس از بررسی‌کننده‌ی داده‌های هرز، مدیر سایت نیز کامنت‌ها را تعدیل کند. فرآیند رویه‌ای مشابه زیر خواهد داشت:

  • هنگامی که یک کامنت ارسال شد، با وضعیت submitted شروع کن؛
  • بگذار که بررسی‌کننده‌ی داده‌های هرز، کامنت را تحلیل کند و وضعیت را به یکی از وضعیت‌های potential_spam، ham یا rejected تغییر دهد.
  • اگر کامنت رد نشد، صبر کن تا مدیر وب‌سایت با تغییر وضعیت به published یا rejected، تصمیم بگیرد که آیا کامنت به اندازه‌ی کافی خوب است یا خیر.

پیاده‌سازی این منطق خیلی پیچیده نیست، اما شما می‌توانید تصور کنید که اضافه‌شدن قواعد بیشتر، پیچیدگی را به میزان زیادی افزایش خواهد داد. ما می‌توانیم به جای اینکه خودمان این منطق را کدنویسی کنیم، از کامپوننت جریان‌کار سیمفونی اسفاده کنیم:

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
../_images/workflow.png

توجه

فرمان dot بخشی از مجموعه ابزار Graphviz است.

استفاده از یک جریان‌کار

منطق موجود در رسیدگی‌کننده‌ی پیغام را با جریان‌کار جایگزین کنید:

patch_file
 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
49
50
51
52
53
54
55
56
57
58
59
60
--- a/src/MessageHandler/CommentMessageHandler.php
+++ b/src/MessageHandler/CommentMessageHandler.php
@@ -6,19 +6,28 @@ use App\Message\CommentMessage;
 use App\Repository\CommentRepository;
 use App\SpamChecker;
 use Doctrine\ORM\EntityManagerInterface;
+use Psr\Log\LoggerInterface;
 use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
+use Symfony\Component\Messenger\MessageBusInterface;
+use Symfony\Component\Workflow\WorkflowInterface;

 class CommentMessageHandler implements MessageHandlerInterface
 {
     private $spamChecker;
     private $entityManager;
     private $commentRepository;
+    private $bus;
+    private $workflow;
+    private $logger;

-    public function __construct(EntityManagerInterface $entityManager, SpamChecker $spamChecker, CommentRepository $commentRepository)
+    public function __construct(EntityManagerInterface $entityManager, SpamChecker $spamChecker, CommentRepository $commentRepository, MessageBusInterface $bus, WorkflowInterface $commentStateMachine, LoggerInterface $logger = null)
     {
         $this->entityManager = $entityManager;
         $this->spamChecker = $spamChecker;
         $this->commentRepository = $commentRepository;
+        $this->bus = $bus;
+        $this->workflow = $commentStateMachine;
+        $this->logger = $logger;
     }

     public function __invoke(CommentMessage $message)
@@ -28,12 +37,21 @@ class CommentMessageHandler implements MessageHandlerInterface
             return;
         }

-        if (2 === $this->spamChecker->getSpamScore($comment, $message->getContext())) {
-            $comment->setState('spam');
-        } else {
-            $comment->setState('published');
-        }
+
+        if ($this->workflow->can($comment, 'accept')) {
+            $score = $this->spamChecker->getSpamScore($comment, $message->getContext());
+            $transition = 'accept';
+            if (2 === $score) {
+                $transition = 'reject_spam';
+            } elseif (1 === $score) {
+                $transition = 'might_be_spam';
+            }
+            $this->workflow->apply($comment, $transition);
+            $this->entityManager->flush();

-        $this->entityManager->flush();
+            $this->bus->dispatch($message);
+        } elseif ($this->logger) {
+            $this->logger->debug('Dropping comment message', ['comment' => $comment->getId(), 'state' => $comment->getState()]);
+        }
     }
 }

منطق جدید به این صورت خوانده می‌شود:

  • اگر برای کامنت موجود در پیغام، تحولِ accept در دسترس است، هرز‌بودن محتوای آن را بررسی کن؛
  • بسته به نتیجه‌ی این بررسی، تحول (transition) صحیح را برای اعمال‌کردن انتخاب کن؛
  • apply() را فراخوانی کن تا از طریق فراخوانی متد setState()، شیءِ Comment را به‌روزرسانی کند؛
  • flush() را فرخوانی کن تا تغییرات در پایگاه‌داده اعمال شود؛
  • پیغام را بازاعزام (Re-dispatch) کن تا اجازه داده شود که جریان‌کار دوباره به کار بیافتد؛

از آنجایی که هنوز اعتبارسنجی مدیر را پیاده‌سازی نکرده‌ایم، دفعه‌ی بعدی که پیغام مصرف شود، «Dropping comment message» لاگ خواهد شد.

بیایید تا قبل از یک پیاده‌سازی واقعی در فصل آینده، فعلاً یک اعتبارسنجی خودکار را پیاده‌سازی کنیم:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
--- a/src/MessageHandler/CommentMessageHandler.php
+++ b/src/MessageHandler/CommentMessageHandler.php
@@ -47,6 +47,9 @@ class CommentMessageHandler implements MessageHandlerInterface
             $this->entityManager->flush();

             $this->bus->dispatch($message);
+        } elseif ($this->workflow->can($comment, 'publish') || $this->workflow->can($comment, 'publish_ham')) {
+            $this->workflow->apply($comment, $this->workflow->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 را اجرا کنید و یک کامنت در جلوی صحنه ایجاد کنید تا تمام وقوع تحولات را یکی پس از دیگری ببینید.


  • « Previous گام 18: پیش به سوی ناهمزمانی (Async)
  • Next » گام 20: ارسال رایانامه به مدیران

This work, including the code samples, is licensed under a Creative Commons BY-NC-SA 4.0 license.