Unleashing the Power of Finite State Machines with Everlution/fsm - Symfony

Sakshi Goraniya
Simform Engineering
4 min readJan 2, 2024

Elevate app performance with Finite State Machines for a responsive user experience and heightened visibility in dynamic workflows.

What is a Finite State Machine?

A Finite State Machine (FSM) is a powerful tool that models and manages the state transitions of objects in a system. It is particularly useful in event-driven systems where entities exist in various states.

Benefits and Usage:

A Finite State Machine (FSM) is a powerful tool that models and manages the state transitions of objects in a system and has versatile applications across fields like:

  • Software Engineering: User Interface Design, Compiler Construction
  • Embedded Systems: Process Control, Communication Protocols
  • Networking: Protocol Design (e.g., TCP)
  • Hardware Design: Digital Circuit Design
  • Game Development: Character AI
  • Automated Systems: Manufacturing Processes, Traffic Control
  • Business Process Modeling: Workflow Management
  • Natural Language Processing: Language Parsing
  • Telecommunications: Call Processing

In this tutorial, we’ll explore how to integrate and implement a robust Finite State Machine in your Symfony project using the everlution/fsm library. This library is lightweight, easy to use, and perfect for Symfony applications dealing with stateful entities.

You can use this library in your Symfony, Laravel, or any PHP project.

Implementing Finite State Machine in Symfony

Prerequisites

Before we begin, ensure you have a Symfony project set up and Composer installed.

Step 1: Install everlution/fsm

Use Composer to install everlution/fsm. Open your terminal and run:

composer require everlution/fsm

Step 2: Create a Symfony Entity

Let’s assume you want to manage the state of an Task entity in your Symfony application. Create a Task entity with the necessary properties and annotations:

// src/Entity/Task.php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Everlution\Fsm\FsmTrait;

/**
* @ORM\Entity
*/
class Task
{
use FsmTrait;

// ... (Existing properties)

public function __construct()
{
// Initialize the state machine with the initial state
$this->initializeFsm('pending');
}
}

Step 3: Finite State Machine Configuration

Now, integrate FSMs into the Task entity with a simple configuration. For the sake of this example, we'll define two states (pending and completed) and a transition (complete_task) that transitions a task from the pending state to the completed state.

// src/Fsm/TaskFsm.php

use Everlution\Fsm\Model\Event;
use Everlution\Fsm\Model\Guard;

class TaskFsm extends AbstractFsm
{
// ... (Existing code)

private function initTransitions()
{
$this->addTransition(
new Transition(
self::TRANSITION_COMPLETE_TASK,
self::STATE_PENDING,
self::STATE_COMPLETED,
[
new Guard(
'can_complete_task',
function (Task $task) {
// Add custom logic to determine if the task can be completed
return !$task->isArchived();
}
),
],
[
new Event(
'task_completed',
function (Task $task) {
// Add custom logic to execute when the task is completed
$task->setCompletedAt(new \DateTime());
}
),
]
)
);
}

// ... (Existing code)
}

In this configuration, we’ve added a guard (can_complete_task) to the TRANSITION_COMPLETE_TASK transition, ensuring that the task can only be completed if it is not archived. Additionally, an event (task_completed) is triggered when the task is completed, allowing you to execute custom logic.

Step 4: Symfony Controller Integration

Enhance the Symfony controller by adding more sophisticated error handling, authorization checks, and a dedicated view for displaying completed tasks.



// src/Controller/TaskController.php

class TaskController extends AbstractController
{
/**
* @Route("/task/{id}/complete", name="task_complete", methods={"POST"})
*/
public function completeTask(Task $task): Response
{
// Ensure the user has permission to complete tasks
$this->denyAccessUnlessGranted('COMPLETE_TASK', $task);

try {
// Use the FSM to transition the task to the completed state
$fsm = $this->get(TaskFsm::class);
$fsm->apply($task, TaskFsm::TRANSITION_COMPLETE_TASK);

// Persist the changes to the database
$entityManager = $this->getDoctrine()->getManager();
$entityManager->flush();

// Redirect to the task details page
return $this->redirectToRoute('task_show', ['id' => $task->getId()]);
} catch (\Exception $e) {
// Handle transition failure, log the error, and display a user-friendly message
$this->addFlash('error', 'Failed to complete the task.');
$this->get('logger')->error('Failed to complete task: ' . $e->getMessage());

// Redirect to the task details page
return $this->redirectToRoute('task_show', ['id' => $task->getId()]);
}
}

/**
* @Route("/task/{id}", name="task_show", methods={"GET"})
*/
public function show(Task $task): Response
{
// Render the task details page with appropriate handling for completed tasks
return $this->render('task/show.html.twig', [
'task' => $task,
'isCompleted' => $task->getState() === TaskFsm::STATE_COMPLETED,
]);
}
}

Key Takeaways

  1. Enhanced Robustness and Maintainability: Fsm’s Advanced features like guards, events, and improved error handling elevate the overall robustness and maintainability of Finite State Machines in Symfony applications.
  2. Improved User Experience and Workflow Visibility: The strategic enhancement not only ensures a responsive user experience but also increases visibility within dynamic workflows, contributing to a more streamlined and effective application.
  3. Synergizing everlution/fsm and Symfony Features: The combination of the everlution/fsm library with Symfony’s powerful features forms a potent toolkit for managing complex state transitions in PHP projects.
  4. Empowerment for Developers: This approach empowers developers to create flexible and efficient event-driven systems that can adapt to diverse application requirements, providing a solid foundation for future development.
  5. Confidence in State Transitions: A comprehensive approach, guided by everlution/fsm library and Symfony’s documentation on FSM integration, instills confidence in developers for navigating and controlling state transitions in Symfony-based PHP applications.

Conclusion

By integrating everlution/fsm with Symfony, you lay a solid foundation for managing state transitions in PHP projects. You can customize this example to fit your project’s specific needs. For detailed guidance, refer to everlution/fsm and Symfony’s documentation on FSM integration.

Follow Simform Engineering to keep yourself updated with the latest trends in the technology horizon. Follow us: Twitter | LinkedIn

--

--