Onlangs kwam ik voor de eerste keer in aanraking met finite state machines in Laravel terwijl ik onderzoek deed naar een implementatie voor één van onze klanten. In deze blog wil ik kort uitleggen wat zo’n state machine inhoudt en waarvoor we zijn functionaliteiten kunnen gebruiken.

Dus wat is het juist?

Om het in de woorden van wikipedia uit te leggen: een finite state machine is een abstract, wiskundig model voor het gedrag van een systeem waarbij het model bestaat uit een eindig aantal toestanden, overgangen tussen die toestanden en acties.

Simpel gezegd willen we dat onze applicatie een beperkt aantal uitkomsten aanbiedt aan de gebruiker. Tegelijkertijd willen we ook de mogelijkheden beperken om deze uitkomsten te bereiken. Een goed voorbeeld is de flow die je moet doorlopen wanneer je online een bestelling wil afronden. Een logische volgorde is bijvoorbeeld dat een gebruiker eerst zijn persoonsgegevens ingeeft, dan wordt doorgestuurd naar het betalingsplatform en daarna een bevestiging krijgt in zijn mailbox. Je wil uiteraard niet dat de gebruiker de betaalstap kan overslaan en zijn order onmiddellijk kan bevestigen.

Klinkt goed, ik wil een voorbeeld!

Laten we ons bovenstaande voorbeeld uitwerken. Om de state machine te implementeren in onze laravel applicatie gebruik ik de package laravel-state-machine van sebDesign. Het eerste wat we moeten opstellen is een state machine graph, dit laat onze applicatie weten welke flow hij moet volgen en dankzij de package kunnen we dit eveneens onmiddellijk koppelen aan ons laravel model.

<?php

return [
    'order_graph' => [
        'class' => Order::class,
        'graph' => 'order_graph',
        'property_path' => 'state',
        'metadata' => [],
        'states' => [
            'checkout',
            'pending-user-data',
            'pending-payment',
            'finished',
            'cancelled'
        ],
        'transitions' => [
            'create' => [
                'from' => ['checkout'],
                'to' => 'input-user-data'
            ],
            'input-user-data' => [
                'from' => ['pending-user-data'],
                'to' => 'pending-payment'
            ],
            'payment-succeeded' => [
                'from' => ['pending-payment'],
                'to' => 'finished'
            ],
            'payment-failed' => [
                'from' => ['pending-payment'],
                'to' => 'cancelled'
            ]
        ],
    ],
];

Voor deze simpele use-case kunnen we vijf states aanmaken en de bijhorende transities definiëren. Deze transities kunnen we nu gebruiken om onze state te updaten en onze logische flow aan te houden. Dit creëert leesbare en makkelijk onderhoudbare entrypoints voor onze functionaliteiten.

Extra functionaliteiten voor elke transitie

In veel gevallen zal je extra functionaliteiten willen toevoegen voor of na je transities. In de state graph is het mogelijk om callbacks in te stellen waarop je kan inhaken om zo extra code uit te voeren. Je wil de bestelling misschien nog valideren vooraleer je verder gaat naar een volgende stap of misschien wil je automatisch een bevestigingsmail versturen wanneer de betaling geslaagd is? Laten we dieper ingaan op deze twee voorbeelden.

    'callbacks' => [
        'guard' => [
            'quard_on_creating' => [
                'on' => 'create',
                'do' => ,
                'args' => ['object'],
            ],
        ],
        'after' => [
            [
                'on' => 'payment-succeeded',
                'do' => 'event',
                'args' => ['"payment-succeeded"', 'event', 'object'],
            ],
        ]
    ]

In dit stukje code hebben we deze twee voorbeelden uitgewerkt. In het eerste deel willen we vooraleer we om de persoonsgegevens vragen, eerst de bestelling valideren. Hiervoor stellen we een guard in die in MyService de bestelling nogmaals zal nakijken op fouten. Als argument sturen we hier het Orderobject mee dat we wensen te valideren. Wanneer bepaalde producten niet meer beschikbaar zijn, kunnen we op deze manier mogelijks een bestelling stopzetten en de koper hiervan op de hoogte brengen.. 

In het tweede deel, sturen we een event uit wanneer onze betaling geslaagd is. We kunnen hierop makkelijk meerdere subscribers instellen die verschillende functionaliteiten gaan toepassen. Een van deze subscribers kan de bevestigingsmail afhandelen terwijl een andere een notificatie van de nieuwe bestelling geeft aan ons extern boekhoudprogramma. 

Wanneer zou jij het gebruiken?

Dus, wanneer zou ik het gebruiken? Altijd wanneer een entiteit in je code meer dan twee states heeft!

Na mijn eerste contact met deze architectuur heb ik state machines in verschillende projecten geïmplementeerd. In mijn ervaring helpen ze een ingewikkelde flow te verduidelijken. De callbacks bieden mij een goede manier om complexe code op te splitsen en zorgen ervoor dat deze eveneens makkelijk uitbreidbaar is. Dit resulteert in meer onderhoudbare code en zorgt voor een duidelijke seperation of concerns

 

Auteur: Jinse Camps
Architect | Analyst
Jinse Camps

More insights

Cross-platform applicaties met React Native

Nog nooit was het ontwikkelen van native mobiele applicaties zo toegankelijk als vandaag. Bij Codana doen we dit door gebruik te maken het React Native, een open-source framework dat werd ontwikkeld door Meta.

Auteur: Jinse Camps
Architect | Analyst
Jinse Camps
dev

Laracon EU 2024

Een fantastisch leerrijke ervaring om met een hoop Laravel gepassioneerde mensen te inspireren! Iets wat niet gemist kan worden en heel veel voeling geeft met de community. Wat een top evenement! Wie zien we volgende edities? 😮

Auteur: Noah Gillard
PHP / Laravel Developer
Noah Gillard AI generated Face
laracon codana persoon

Een efficiënt datamanagementsysteem voor toerisme

Een TDMS of Tourist Data Management System, is simpelweg een platform dat data uit verschillende bronnen ophaalt, intern al dan niet automatisch verwerkt en deze gegevens terug aanbiedt aan externe platformen.

Auteur: Tom Van den Eynden
Web Architect | Coordinator
Tom Van den Eynden
laptop

Systemen voor gegevensbeheer in toerisme

In dit artikel verkennen we wat een TDMS is, waarom het essentieel is voor de toerisme-industrie, en hoe technologieën zoals Laravel en ElasticSearch het verschil kunnen maken. 

Auteur: Tom Van den Eynden
Web Architect | Coordinator
Tom Van den Eynden
tdms

Beveiliging van Laravel 101

In deze blogpost gaan we dieper in op een aantal veelvoorkomende Laravel beveiligingsfouten.

Auteur: Robbe Reygel
PHP developer
laravel

Test Driven Development - toepassing op een project

TDD, of voluit Test Driven Development, is een aanpak van ontwikkeling waarbij we vertrekken van het schrijven van tests. 

Auteur: Sarah Jehin
PHP developer
Sarah Jehin
development