You are browsing the book for Symfony 5.0 which is not maintained anymore. Code examples might not work anymore, even in a project using Symfony 5.0.
Consider reading the book for Symfony 5.2 instead.
Pas 19: Luarea deciziilor cu Workflow
Luarea deciziilor cu Workflow¶
A avea o stare pentru un model este destul de comun. Starea comentariilor este determinată doar de verificatorul de spam. Ce se întâmplă dacă adăugăm mai mulți factori de decizie?
Am putea dori să lăsăm administratorul site-ului să modereze toate comentariile după verificatorul de spam. Procesul ar fi ceva similar:
- Începe cu o stare
submitted
când un comentariu este trimis de un utilizator; - Lasă verificatorul de spam să analizeze comentariul și schimbă starea pe
potential_spam
,ham
saurespins
; - Dacă nu este respins, așteptă ca administratorul site-ului să decidă dacă comentariul este suficient de bun, comutând statul la
published
saurejected
.
Implementarea acestei logici nu este prea complexă, însă adăugarea mai multor reguli ar crește mult complexitatea. În loc să codificăm logica, putem folosi componenta Symfony Workflow:
1 | $ symfony composer req workflow
|
Descrierea fluxurilor de lucru¶
Fluxul de lucru pentru comentarii poate fi descris în fișierul 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
|
Pentru a valida fluxul de lucru, generează o reprezentare vizuală:
1 | $ symfony console workflow:dump comment | dot -Tpng -o workflow.png
|

Notă
Comanda dot
face parte din utilitarul Graphviz.
Utilizarea unui flux de lucru¶
Înlocuește logica curentă din manipulatorul de mesaje cu fluxul de lucru:
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');
- }
- $this->entityManager->flush();
+ 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->bus->dispatch($message);
+ } elseif ($this->logger) {
+ $this->logger->debug('Dropping comment message', ['comment' => $comment->getId(), 'state' => $comment->getState()]);
+ }
}
}
|
Logică nouă are următoarea structură:
- Dacă tranziția
accept
este disponibilă pentru comentariul din mesaj, verifică dacă nu există spam; - În funcție de rezultat, alege tranziția potrivită pentru a o aplica;
- Apelează
apply()
pentru a actualiza comentariul printr-un apel la metodasetState()
; - Apelează
flush()
pentru a salva modificările în baza de date; - Reexpediază mesajul pentru a permite fluxului de lucru să tranziteze din nou.
Deoarece nu am implementat validarea admin, la următoarea consumare a mesajului va fi logat mesajul „Dropping comment message”.
Să implementăm o validare automată până la următorul capitol:
1 2 3 4 5 6 7 8 9 10 11 12 | --- a/src/MessageHandler/CommentMessageHandler.php
+++ b/src/MessageHandler/CommentMessageHandler.php
@@ -50,6 +50,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()]);
}
|
Execută server symfony:log
și adaugă un comentariu în frontend pentru a vedea toate tranzițiile care se petrec una după alta.
- « Previous Pas 18: Procesând asincron
- Next » Pas 20: Expedierea e-mail-urilor administratorilor
This work, including the code samples, is licensed under a Creative Commons BY-NC-SA 4.0 license.