خطوة 19: إتخاذ القرارات خلال سير العمل

5.0 version
Maintained

إتخاذ القرارات خلال سير العمل

وجود حالة لنموذج أمر شائع إلي حد كبير. حالة التعليق يتم تحديدها عن طريق محقق البيانات الفضولية فقط. ماذا لو أضفنا المزيد من عوامل إتخاذ القرار؟

قد نسمح لمديد الموقع إدارة جميع التعليقات بعد محقق البيانات الفضولية. سوف تكون العملية شئ ما علي نحو:

  • بداية مع حالة ال submitted عندما يقوم المستخدم بإرسال تعليق؛
  • دع محقق البيانات الفضولية ان يقوم بتحليل التعليق ويغير الحالة الي اىً من potential_spam، ham، او rejected؛
  • إن لم يكن مرفوض، إنتظر حتي يقرر مدير الموقع اذا كان التعليق جيدا عن طريق تغير الحالة إلي published او rejected.

تطبيق هذا المنطق ليس معقداً، لكن يمكنك تخيل ان إضافة المزيد من الشروط قد يزيد التعقيد الي حد كبير. بدلاَ من كتابة الرمز البرمجي بنفسنا، يمكننا ان نستخدم مكون سير العمل الخاص بسيمفوني (Symfony Workflow Component):

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

Note

يعتبر امر ال 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 متاح للتعليق في الرسالة، تحقق من البيانات الفضولية؛
  • إستناداً علي النتائج، قم باختيارالانتقال الصحيح لتطبيقه؛
  • قم بالمناداة علي apply() لتعديل التعليق عن طريق المناداة علي منهج ال setState().
  • نادي علي flush()` لحفظ التعديلات في قاعدة البيانات
  • قم بإعادة إرسال الرسالة للسماح لمسار العمل بالانتقال مرة أخري.

وبما اننا لم نقم بكتابة الرمز البرمجي للتحقق من المدير، في المرة التالية التي يتم فيها إستهلاك الرسالة، سوف يتم تسجيل "إسقاط رسالة التعليق (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: الذهاب المتزامن
  • Next » خطوة 20: مراسلة المدراء

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