mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-08-22 23:53:50 +00:00
Refactor workflow classes and forms
- the workflow controller add a context to each transition; - the state of the entity workflow is applyied using a dedicated marking store - the method EntityWorkflow::step use the context to associate the new step with the future destination user, cc users and email. This makes the step consistent at every step. - this allow to remove some logic which was processed in eventSubscribers, - as counterpart, each workflow must specify a dedicated marking_store: ```yaml framework: workflows: vendee_internal: # ... marking_store: service: Chill\MainBundle\Workflow\EntityWorkflowMarkingStore ```
This commit is contained in:
@@ -49,6 +49,4 @@ interface EntityWorkflowHandlerInterface
|
||||
public function isObjectSupported(object $object): bool;
|
||||
|
||||
public function supports(EntityWorkflow $entityWorkflow, array $options = []): bool;
|
||||
|
||||
public function supportsFreeze(EntityWorkflow $entityWorkflow, array $options = []): bool;
|
||||
}
|
||||
|
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Workflow;
|
||||
|
||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||
use Symfony\Component\Workflow\Marking;
|
||||
use Symfony\Component\Workflow\MarkingStore\MarkingStoreInterface;
|
||||
|
||||
final readonly class EntityWorkflowMarkingStore implements MarkingStoreInterface
|
||||
{
|
||||
public function getMarking(object $subject): Marking
|
||||
{
|
||||
if (!$subject instanceof EntityWorkflow) {
|
||||
throw new \UnexpectedValueException('Expected instance of EntityWorkflow');
|
||||
}
|
||||
$step = $subject->getCurrentStep();
|
||||
|
||||
return new Marking([$step->getCurrentStep() => 1]);
|
||||
}
|
||||
|
||||
public function setMarking(object $subject, Marking $marking, array $context = []): void
|
||||
{
|
||||
if (!$subject instanceof EntityWorkflow) {
|
||||
throw new \UnexpectedValueException('Expected instance of EntityWorkflow');
|
||||
}
|
||||
|
||||
$places = $marking->getPlaces();
|
||||
if (1 < count($places)) {
|
||||
throw new \LogicException('Expected maximum one place');
|
||||
}
|
||||
$next = array_keys($places)[0];
|
||||
|
||||
$transitionDTO = $context['context'] ?? null;
|
||||
if (!$transitionDTO instanceof WorkflowTransitionContextDTO) {
|
||||
throw new \UnexpectedValueException(sprintf('Expected instance of %s', WorkflowTransitionContextDTO::class));
|
||||
}
|
||||
|
||||
$subject->setStep($next, $transitionDTO);
|
||||
}
|
||||
}
|
@@ -21,31 +21,13 @@ use Symfony\Component\Workflow\Event\Event;
|
||||
use Symfony\Component\Workflow\Event\GuardEvent;
|
||||
use Symfony\Component\Workflow\TransitionBlocker;
|
||||
|
||||
class EntityWorkflowTransitionEventSubscriber implements EventSubscriberInterface
|
||||
final readonly class EntityWorkflowTransitionEventSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
public function __construct(private readonly LoggerInterface $chillLogger, private readonly Security $security, private readonly UserRender $userRender) {}
|
||||
|
||||
public function addDests(Event $event): void
|
||||
{
|
||||
if (!$event->getSubject() instanceof EntityWorkflow) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var EntityWorkflow $entityWorkflow */
|
||||
$entityWorkflow = $event->getSubject();
|
||||
|
||||
foreach ($entityWorkflow->futureCcUsers as $user) {
|
||||
$entityWorkflow->getCurrentStep()->addCcUser($user);
|
||||
}
|
||||
|
||||
foreach ($entityWorkflow->futureDestUsers as $user) {
|
||||
$entityWorkflow->getCurrentStep()->addDestUser($user);
|
||||
}
|
||||
|
||||
foreach ($entityWorkflow->futureDestEmails as $email) {
|
||||
$entityWorkflow->getCurrentStep()->addDestEmail($email);
|
||||
}
|
||||
}
|
||||
public function __construct(
|
||||
private LoggerInterface $chillLogger,
|
||||
private Security $security,
|
||||
private UserRender $userRender
|
||||
) {}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
@@ -53,7 +35,6 @@ class EntityWorkflowTransitionEventSubscriber implements EventSubscriberInterfac
|
||||
'workflow.transition' => 'onTransition',
|
||||
'workflow.completed' => [
|
||||
['markAsFinal', 2048],
|
||||
['addDests', 2048],
|
||||
],
|
||||
'workflow.guard' => [
|
||||
['guardEntityWorkflow', 0],
|
||||
@@ -99,6 +80,10 @@ class EntityWorkflowTransitionEventSubscriber implements EventSubscriberInterfac
|
||||
|
||||
public function markAsFinal(Event $event): void
|
||||
{
|
||||
// NOTE: it is not possible to move this method to the marking store, because
|
||||
// there is dependency between the Workflow definition and the MarkingStoreInterface (the workflow
|
||||
// constructor need a MarkingStoreInterface)
|
||||
|
||||
if (!$event->getSubject() instanceof EntityWorkflow) {
|
||||
return;
|
||||
}
|
||||
|
@@ -23,7 +23,13 @@ use Symfony\Component\Workflow\Registry;
|
||||
|
||||
class NotificationOnTransition implements EventSubscriberInterface
|
||||
{
|
||||
public function __construct(private readonly EntityManagerInterface $entityManager, private readonly \Twig\Environment $engine, private readonly MetadataExtractor $metadataExtractor, private readonly Security $security, private readonly Registry $registry) {}
|
||||
public function __construct(
|
||||
private readonly EntityManagerInterface $entityManager,
|
||||
private readonly \Twig\Environment $engine,
|
||||
private readonly MetadataExtractor $metadataExtractor,
|
||||
private readonly Security $security,
|
||||
private readonly Registry $registry
|
||||
) {}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
@@ -85,7 +91,10 @@ class NotificationOnTransition implements EventSubscriberInterface
|
||||
'dest' => $subscriber,
|
||||
'place' => $place,
|
||||
'workflow' => $workflow,
|
||||
'is_dest' => \in_array($subscriber->getId(), array_map(static fn (User $u) => $u->getId(), $entityWorkflow->futureDestUsers), true),
|
||||
'is_dest' => \in_array($subscriber->getId(), array_map(
|
||||
static fn (User $u) => $u->getId(),
|
||||
$entityWorkflow->getCurrentStep()->getDestUser()->toArray()
|
||||
), true),
|
||||
];
|
||||
|
||||
$notification = new Notification();
|
||||
|
@@ -20,7 +20,13 @@ use Symfony\Component\Workflow\Registry;
|
||||
|
||||
class SendAccessKeyEventSubscriber
|
||||
{
|
||||
public function __construct(private readonly \Twig\Environment $engine, private readonly MetadataExtractor $metadataExtractor, private readonly Registry $registry, private readonly EntityWorkflowManager $entityWorkflowManager, private readonly MailerInterface $mailer) {}
|
||||
public function __construct(
|
||||
private readonly \Twig\Environment $engine,
|
||||
private readonly MetadataExtractor $metadataExtractor,
|
||||
private readonly Registry $registry,
|
||||
private readonly EntityWorkflowManager $entityWorkflowManager,
|
||||
private readonly MailerInterface $mailer
|
||||
) {}
|
||||
|
||||
public function postPersist(EntityWorkflowStep $step): void
|
||||
{
|
||||
@@ -32,7 +38,7 @@ class SendAccessKeyEventSubscriber
|
||||
);
|
||||
$handler = $this->entityWorkflowManager->getHandler($entityWorkflow);
|
||||
|
||||
foreach ($entityWorkflow->futureDestEmails as $emailAddress) {
|
||||
foreach ($step->getDestEmail() as $emailAddress) {
|
||||
$context = [
|
||||
'entity_workflow' => $entityWorkflow,
|
||||
'dest' => $emailAddress,
|
||||
|
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Workflow;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Component\Workflow\Transition;
|
||||
|
||||
/**
|
||||
* Context for a transition on an workflow entity.
|
||||
*/
|
||||
class WorkflowTransitionContextDTO
|
||||
{
|
||||
/**
|
||||
* a list of future dest users for the next steps.
|
||||
*
|
||||
* This is in used in order to let controller inform who will be the future users which will validate
|
||||
* the next step. This is necessary to perform some computation about the next users, before they are
|
||||
* associated to the entity EntityWorkflowStep.
|
||||
*
|
||||
* @var array|User[]
|
||||
*/
|
||||
public array $futureDestUsers = [];
|
||||
|
||||
/**
|
||||
* a list of future cc users for the next steps.
|
||||
*
|
||||
* @var array|User[]
|
||||
*/
|
||||
public array $futureCcUsers = [];
|
||||
|
||||
/**
|
||||
* a list of future dest emails for the next steps.
|
||||
*
|
||||
* This is in used in order to let controller inform who will be the future emails which will validate
|
||||
* the next step. This is necessary to perform some computation about the next emails, before they are
|
||||
* associated to the entity EntityWorkflowStep.
|
||||
*
|
||||
* @var array|string[]
|
||||
*/
|
||||
public array $futureDestEmails = [];
|
||||
|
||||
public ?Transition $transition = null;
|
||||
|
||||
public string $comment = '';
|
||||
|
||||
public function __construct(
|
||||
public EntityWorkflow $entityWorkflow
|
||||
) {}
|
||||
|
||||
#[Assert\Callback()]
|
||||
public function validateCCUserIsNotInDest(ExecutionContextInterface $context, $payload): void
|
||||
{
|
||||
foreach ($this->futureDestUsers as $u) {
|
||||
if (in_array($u, $this->futureCcUsers, true)) {
|
||||
$context
|
||||
->buildViolation('workflow.The user in cc cannot be a dest user in the same workflow step')
|
||||
->atPath('ccUsers')
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user