Máquinas de estados finitos
Tags: tip, desarrollo, php
Published: 2021-02-19 12:30:16

---
title: Máquinas de estados finitos
published: true
tags: ["tip", "desarrollo", "php"]
canonical_url: https://marceloandrader.github.io/blog/2021/02/19/m%C3%A1quinas-de-estado-finito-fsm/
---

Una cosa que aprendí luego de cometer el mismo error varias veces en proyectos pasados es dejar de usar `booleans` en los diseños de las tablas de la base de datos. Estos te dan cierta facilidad al inicio pero son muy difícil de usarlos a futuro. Por ejemplo una tabla `usuarios` con el campo `estado` con valores `true` es activo y `false` es inactivo parece muy inofensiva; el problema viene cuando queremos agregar un tercer estado como por ejemplo  `reseteando` para identificar que usuarios están reseteando el password y la solución más fácil en ese momento es agregar un nuevo campo llamado `reseteando` como **boolean**. Pero cuando quieres hacer una consulta a esa tabla de los usuarios activos, ahora tienes que verificar que reseteando también sea `false`. Y esto pasa en muchos otros procesos, como facturas, documentación, etc.

Muchos de los procesos del negocio casi siempre tienen un flujo de trabajo asociado, y los estados no son blanco o negro, la solución que ha funcionado para mí en ya algunos proyectos, es usar máquinas de estados finitos o FSM por sus siglas en inglés.

Existen muchas librerías que permiten usar una FSM en diferentes lenguajes de programación. En el caso de PHP la que uso personalmente es: [yohang/finite](https://packagist.org/packages/yohang/finite) tiene un respetable número de descargas en packagist y es agnóstica al framework, la he usado con laravel, phalcon y symfony sin ningún problema. 

En una FSM se define una configuración para una entidad, esta debe tener `estados`, `transiciones` y opcionalmente `llamadas`.

**Estados** (states) como su nombre lo indica son los posibles estados en los que un objeto de la entidad puede estar. En nuestro ejemplo de usuarios, este puede tener los siguiente estados: activo, inactivo, reseteando.

**Transiciones** (transitions) son las posibles acciones que pueden hacer que un objeto cambie de un estado a otro. En nuestro ejemplo las transiciones pueden ser `activar` que va de inactivo a activo, `desactivar` que va de activo a inactivo y `resetear` que va de inactivo o activo a reseteando y `reestablecer` que va de reseteando a activo.

**Llamadas** (callbacks) son funciones del objeto que se llaman cuando una transición se ejecuta, esto me ha sido útil por ejemplo para llevar un control de cambios en el objeto, cuándo se hizo la transición y quién la ejecutó.

Una vez realizada la configuración, el API para verificar que transiciones puede realizar el objeto son:

```php
$user->getCurrentState(); // por ejemplo ‘activo’

$user->can(‘activar’); // false porque está en estado activo solo puede ir a reseteando
$user->can(‘desactivar’); // true si se puede realizar esta transición de acuerdo a la configuración

$user->apply(‘desactivar’); // se realiza el cambio
$user->getCurrentState(); // ahora es ‘inactivo’

$user>getCurrentState()->getTransitions(); // muestra que posibles transiciones tiene disponible, en el caso de inactivo puede ir solo a ‘activar’
```

Para mayor información la [documentación de yohang/finite](https://finite.readthedocs.io/en/master/index.html) es muy completa.