Créer un state machine de Symfony a partir d'un DTO

Créer un state machine de Symfony a partir d'un DTO
31/05/2023
- Créée
31/05/2023
Symfony
<?php

namespace App\Service;

use App\Entity\Dto;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Workflow\Definition;
use Symfony\Component\Workflow\MarkingStore\MethodMarkingStore;
use Symfony\Component\Workflow\Metadata\InMemoryMetadataStore;
use Symfony\Component\Workflow\Registry;
use Symfony\Component\Workflow\StateMachine;
use Symfony\Component\Workflow\SupportStrategy\InstanceOfSupportStrategy;
use Symfony\Component\Workflow\Transition;
use Symfony\Component\Workflow\Validator\StateMachineValidator;

class EntityToStateMachine
{
    public const METADATA_DTO = 'dto';

    private EventDispatcherInterface $dispatcher;
    private Registry $registry;

    public function __construct(
        EventDispatcherInterface $dispatcher,
        Registry                 $registry
    )
    {
        $this->dispatcher = $dispatcher;
        $this->registry   = $registry;
    }

    public function getStateMachine(Dto $dto, $subject = null): StateMachine
    {
        $markingStore = new MethodMarkingStore(true, 'currentPlace');

        $states = $this->getStates($dto);

        $initialPlace = $dto->getInitialPlace();

        $stateTransitions = $this->getTransitions($dto);

        $metaData = (new InMemoryMetadataStore([
            self::METADATA_DTO => $dto,
        ]));

        // No validation is done upon initialization
        $definition = new Definition(
            $states,
            $stateTransitions,
            $initialPlace->getUniqueId(),
            $metaData
        );

        // Throws InvalidDefinitionException in case of an invalid definition
        (new StateMachineValidator())->validate($definition, $dto->getSlug());

        $stateMachine = new StateMachine(
            $definition,
            $markingStore,
            $this->dispatcher,
            $dto->getSlug(),
            null
        );

        $this->registry->addWorkflow($stateMachine, new InstanceOfSupportStrategy(WorkflowInterface::class));

        if ($subject) {
            if(null === $subject->getCurrentPlace()){
                $subject->setCurrentPlace($initialPlace->getUniqueId());
            }
            $stateMachine->getMarking($subject);
        }

        return $stateMachine;
    }

    private function getStates(Dto $dto): array
    {
        return array_map(static function ($place) {
            return $place->getUniqueId();
        }, $dto->getPlaces());
    }

    private function getTransitions(Dto $dto): array
    {
        return array_map(static function ($transition) {
            $from = $transition->getFromPlace()->getUniqueId();
            $to   = $transition->getToPlace()->getUniqueId();
            return new Transition(
                $transition->getUniqueId(),
                [$from],
                [$to]
            );
        }, $dto->getTransitions());
    }
}