Symfony 8.2 Nouveauté Publié le

Décrire un workflow par attributs

#[AsWorkflow] et #[Transition] : la machine à états, ses transitions et ses gardes tiennent dans une seule classe, à la place du YAML.

Notions abordées : WorkflowGardes

Nouveau dans Symfony 8.2 (en développement). Un workflow se décrivait en YAML : ses places, ses transitions, l'objet qu'il suit. Ses gardes et ses écouteurs vivaient ailleurs, dans des classes qui le désignaient par son nom, et le code qui le faisait avancer répétait les noms des transitions en chaînes de caractères. Symfony 8.2 réunit tout dans une classe.

Avant

framework:
    workflows:
        ticket:
            type: state_machine
            supports: [App\Model\Ticket]
            marking_store: { type: method, property: marking }
            initial_marking: ouvert
            transitions:
                prendre_en_charge: { from: ouvert, to: en_cours }
                # …
public function suivi(#[Target('ticket')] WorkflowInterface $workflow): Response
{
    $workflow->apply($ticket, 'prendre_en_charge');

Depuis Symfony 8.2

#[AsWorkflow(supports: Ticket::class)]
final class TicketWorkflow
{
    use WorkflowTrait;

    #[Transition(from: 'ouvert', to: 'en_cours')]
    public const PRENDRE_EN_CHARGE = 'prendre_en_charge';

    #[AsGuardListener(transition: self::PRENDRE_EN_CHARGE)]
    public function verifier(GuardEvent $event): void { /* … */ }
}
public function suivi(TicketWorkflow $workflow): Response
{
    $workflow->apply($ticket, TicketWorkflow::PRENDRE_EN_CHARGE);

#[AsWorkflow] fait de la classe un workflow, nommé d'après elle : TicketWorkflow donne ticket. C'est une machine à états par défaut (type: WorkflowType::Workflow en fait un workflow, où un objet peut occuper plusieurs places à la fois), et le marquage se range dans la propriété marking du sujet. Chaque constante marquée #[Transition] est une transition ; les places s'en déduisent, et la place d'entrée (from) de la première transition devient la place initiale. Une énumération peut aussi fournir les places, avec places: et #[Place] pour leurs métadonnées.

La classe reste un service ordinaire : elle reçoit ses dépendances et porte ses propres écouteurs, qui n'ont plus besoin de nommer le workflow. Avec WorkflowTrait, elle expose aussi les méthodes d'un workflow : on l'injecte, et ses constantes remplacent les chaînes.

À faire

Le service client suit ses tickets avec une machine à états décrite dans config/packages/workflow.yaml, et sa garde, qui interdit de résoudre un ticket sans réponse, vit déjà dans TicketWorkflow. L'aperçu /ticket déroule la vie d'un ticket et montre, à chaque étape, les transitions possibles.

  1. Décrivez le workflow ticket dans TicketWorkflow, une constante par transition, et retirez-le du YAML.
  2. Laissez la garde s'appliquer au workflow de sa classe, sans le nommer.
  3. Dans TicketController, recevez TicketWorkflow et remplacez les noms de transitions par ses constantes.

L'aperçu doit afficher exactement le même parcours qu'avant.

D'où vient la fonctionnalité : la pull request d'origine.