viernes, 27 de marzo de 2015

Instalación de Composer

Composer es el gestor de paquetes para PHP del momento y, de hecho, es una muy buena herramienta para mantener controladas las dependencias de un un proyecto PHP. No tienes más que definir un archivo composer.json con las dependencias del proyecto y ya tienes un instalador limpio y bastante sofisticado.
Yo lo he instalado de forma global en el equipo:
curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer
Me resulta más cómodo que hacerlo localmente por proyecto.
Por el momento, este es el contenido del composer/json
{
    "name": "cakephpilia/academia",
    "description": "Educational platform",
    "homepage": "http://miralba.org",
    "type": "project",
    "license": "MIT",
    "require": {
        "php": ">=5.3"
    },
    "require-dev": {
    },

    "suggest": {
        "phpunit/phpunit": "Allows automated tests to be run without system-wide install."
    },
    "autoload": {
        "psr-4": {
            "Academia\\": "."
        }
    },
    "minimum-stability": "dev",
    "prefer-stable": true
}
De momento, lo más interesante es la configuración del autoloader ya que todavía no hay dependencias que cargar.
Composer incluye varias clases con las que configura un autoloader de los paquetes instalados y que se basa en el uso de espacios de nombres que coincidan con la organización del sistema de archivos (PSR-4). Para usarlo en el proyecto, básicamente tienes que indicar una clave "autoload" e indicar la relación entre la raíz del espacio de nombres y el directorio en el que se encuentra.
En mi caso, la raíz del espacio de nombres es "Academia". La doble barra invertida \\ es necesaria porque debes terminar la cadena con el separador de espacio de nombres "\" el cual, por ser un carácter especial, debe ir "escapado", precisamente con la barra invertida.
Finalmente, para usar el autoloader en el proyecto, he creado un archivo bootstrap.php provisional:


 # Uses composer autoload

 require_once __DIR__.'/vendor/autoload.php';
Este archivo se lo indico a phpunit para que puedan cargarse las clases y así no las importo explícitamente en los tests.
phpunit --bootstrap bootstrap.php --colors --verbose tests

Instalando PHP 5.5 en Snow Leopard

Mi máquina de trabajo es un iMac (iMac5,1) late 2006 corriendo con Snow Leopard. Viene preinstalado con PHP 5.3.10, pero me interesaba tener una versión más reciente para aprovechar algunas de las mejoras del lenguaje relacionadas con OOP.
Para instalarlo he ido por la vía fácil, que es utilizar los paquetes de PHP 5.3 to 5.6 for OS X 10.6 to 10.10 as binary package, que vienen ya configurados con lo que podría necesitar.

Instalación

Para ello, no hay más que teclear esto en terminal. Mi máquina no puede con la versión 5.6 que es la estable a la hora de escribir esto.

curl -s http://php-osx.liip.ch/install.sh | bash -s 5.5

Esta instalación no sobrescribe la original, así que para evitar interferencias y diferentes versiones en el servidor web y en la consola, los siguientes pasos van a ser "inutilizar" la versión anterior y configurar apache para que use la más reciente. El PHP preinstalado está en /usr/bin, el nuevo, en /usr/local/php5/bin, así que voy a cambiar el nombre del "viejo" PHP para que no pueda utilizarse

cd /usr/bin
sudo mv php php_old
El siguiente paso es hacer que la nueva versión de PHP esté en $PATH, así que lo añadimos para la sesión actual:
export PATH="/usr/local/php5/bin/:$PATH"
Esta línea también la añado en el archivo .profile para las futuras sesiones:
cd ~
pico .profile
...
export PATH="/usr/local/php5/bin/:$PATH"
...
Nos queda un último apartado, que es decirle a Apache (también viene preinstalado en el sistema) que utilice el binario adecuado de PHP. El caso es que la instalación de que acabamos de realizar se ocupa de eso, añadiendo archivos de configuración extra aparte de nuestro /etc/apache2/httpd.conf. En este caso, lo que tenemos que hacer es comentar la siguiente línea y reiniciar Apache (cosa que puedes hacer desactivando y activando Compartir Web en el panel de preferencias del sistema Compartir),
#LoadModule php5_module        libexec/apache2/libphp5.so
Si, justamente lo que hacemos es deshabilitar la carga de PHP por defecto que hace Apache, para dejarla en manos de un archivo de configuración añadido que le indica la nueva ubicación de la biblioteca. Lo puedes ver abriendo /etc/apache2/other.
Y con esto, tenemos sincronizadas las versiones web y cli de PHP. Puedes comprobarlo en la consola con:
php -v
Una última anotación es que me he dado cuenta de que puede ser interesante incluir en el proyecto los archivos de configuración de Apache y el php.ini para tenerlos a mano como referencia o para comprobar y cambiar ajustes que en algún momento nos interesen.




Domain Driven Design en una aplicación de gestión académica

Voy a iniciar el desarrollo de una aplicación de gestión académica para un colegio. El dominio es lo suficientemente complejo como para necesitar un cambio de metodología respecto a mi modo de trabajo actual.
Hasta ahora, he trabajado a partir del framework CakePHP en su versión 1.3 con buen resultado. Sin embargo, ha llegado un momento en el que me siento limitado por el propio framework. Por lo que he visto de la versión 3, las cosas han mejorado mucho en todos los aspectos en los que he percibido esas limitaciones, pero...
El caso es que llevo unas cuantas semanas estudiando conceptos de patrones de diseño, arquitectura limpia y diseño orientado por el dominio y esto ha influido en mi manera de percibir la forma en que estoy trabajando y la forma en que quiero trabajar a partir de ahora. Vamos al grano.

Academia

El nombre provisional de mi proyecto es "Academia".
Se trata de una aplicación de gestión académica para un colegio. Esto significa que el dominio de la aplicación es mantener la información académica de los alumnos de un colegio, es decir, sus calificaciones en las diversas asignaturas, niveles y etapas que cursan, así como otras incidencias relacionadas con su actividad, como pueden ser las faltas de asistencia o puntualidad, el uso de servicios no escolares del colegio (el comedor, por ejemplo) y otras circunstancias.
Un requisito del proyecto es que las familias de los alumnos, y ellos mismos, puedan acceder a la información de sus hijos, que puedan comunicarse con el profesorado a través de este medio y que puedan acceder a diversos informes y trámites a través de una aplicación web.
Por otro lado, es necesario poder gestionar la organización académica del centro: etapas, niveles, asignaturas, clases, aulas, etc. Esto además, tiene que tener cierta flexibilidad ya que hay cambios legales y organizativos prácticamente todos los cursos. Pueden ser cambios en los planes de estudio, pero también cambios internos, como el hecho de que cada año escolar se reorganiza las tutorías y la docencia.

Primeros pasos

De momento, no voy a tomar ninguna decisión técnica. Es decir, no voy a elegir un framework o una solución concreta de persistencia. Uno de los problemas que puedo tener a la hora de hacer este cambio de metodología de desarrollo es tener un modo de pensar condicionado por lo que son detalles de implementación. Por otro lado, en una arquitectura limpia todos estos detalles estarían aislados de tal modo que podrían cambiarse por otras soluciones, reescribiendo sólo aquellos adaptadores necesarios.
En el Diseño Orientado por el Dominio hay que empezar por el dominio de la aplicación, entender qué es aquello de lo que se ocupa la aplicación y el primer paso para lograrlo es definir el Lenguaje Ubicuo.

Entorno

Con todo, algunas decisiones técnicas sí he empezado a tomarlas:
La primera es el lenguaje, que será PHP 5.5, he aquí un método para instalarlo.
La segunda es utilizar Composer para gestionar las dependencias del proyecto. Composer es una opción que se está generalizando y todos los grandes proyectos, bibliotecas, frameworks, etc. le están dando soporte. Además, Composer proporciona una solución de autoloader basada en Espacios de Nombres que me viene muy bien.
La tercera es utilizar PHPUnit para los test unitarios. Hasta ahora, venía usando SimpleTest a través de la CakeTest, pero está claro que PHPUnit es el camino a seguir en el sentido de que, al igual que Composer, se está convirtiendo en un estándar.
Voy a poner dos entradas acerca de la instalación de Composer y PHPUnit, así como de los primeros pasos que he ido dando con estas herramientas.

Lo siguiente...

En la próxima entrada tratará sobre el lenguaje ubicuo de Academia y los primeros problemas con el modelado del dominio.

Arquitectura limpia y Diseño dirigido por el Dominio

Referencias:

The clean architecture, Robert C. Martin
Implementing the Onion Architecture in PHP, Por Kristofer Wilson
The clean architecture in PHP, presentación por Kristofer Wilson
Onion architecture and Zend framework, por Igor Vorobiov
The onion architecture, por Jeffrey Palermo
Well designed application arquitectures, por Jeremy Bush
Building an object model, por Benjamín Eberlei
Clean architecture using DDD layering in PHP, presentación por Leonardo Proietti

Lecturas sobre Diseño dirigido por el dominio

Domain Driven Design, por Chris Peters
Domain-Driven Design and MVC Architectures, por Federico Cargnelutti
Domain Driven Design and Development In Practice, por Srini Penchikala
Domain-driven Design Example, por Mirko Sertic
Domain-driven Design - Overview and building blocks, por Mirko Sertic

Una explicación de DAO pattern
Decoupling models from the database: Data Access Object pattern in PHP, por Jani Hartikainen

Cómo diferenciar entre Entidades y Value Objects
What is the difference between Entities and Value Objects?, por Philip Brown
DDD: Help me further understand Value Objects and Entities, pregunta en StackOverflow
A Discussion on Domain Driven Design: Entities. por Joe Ocampo

Implementaciones de seguridad y DDD
Don't do role based authorization checks

martes, 17 de marzo de 2015

Patrón Observador (Observer pattern)

Fuentes:
Observer pattern: the CakePHP way, por Zen of Coding
Observer Design pattern, por Sourcemaking
Patrones de comportamiento (VI) Patrón Observer, por David García

El patrón Observador es la respuesta a la situación en la que necesitas que ciertos objetos (llamados observers) sean notificados de los cambios en otro (llamado subject). Para esto, se suscriben a los anuncios del objeto observado y actúan cuando reciben una notificación que sepan manejar.
De este modo se logra que el objeto observado no tenga que preocuparse de cuestiones que no tienen que ver con su propia responsabilidad, como podrían ser enviar un email o actualizar datos de otro objeto.
En programación de escritorio, por ejemplo, el uso prototípico es que las vistas de un modelo se actualicen si cambian datos en el mismo. El modelo no tiene que preocuparse de actualizar la vista, sino que es la vista la que "observa" al modelo para ver si ha cambiado y actuar en consecuencia.
En el caso de una aplicación web escrita en PHP con una arquitectura MVC el patrón observador puede ser muy útil para gestionar cuestiones como las notificaciones por email a los usuarios, registro de ciertas actividades, etc, que no encajan en la capa del modelo y que tampoco es que tengan un encaje muy claro en el controlador.

La situación

El patrón Observador se utiliza cuando necesitamos que uno o más objetos sepan que otro objeto ha cambiado de algún modo y actúen en consecuencia. Es adecuado para que la aplicación pueda reaccionar ante ciertos eventos que no se sabe cuándo van a ocurrir.
Por ejemplo, imaginemos una aplicación web de un colegio en la que los visitantes puedan solicitar una plaza para estudiar. Los visitantes cubrirían un formulario y, una vez validado, el sistema podría notificar por email a los propios interesados que la solicitud se ha procesado correctamente y a miembros de la administración del centro para que sepan que se ha recibido una solicitud e iniciar el proceso de trámite de la misma. Este ejemplo puede solventarse perfectamente con el uso del patrón observador.

El patrón

El patrón observer utiliza dos interfaces:
Una es para ser implementada por las clases que pueden vas a ser observadas (el subject) y tiene métodos para que los observadores (observers) se suscriban (attach), se den de baja (detach) y sean notificados por el objeto observado (notify). La Biblioteca Standard de PHP (SPL) dispone de la interfaz SplSubject que encaja perfectamente aquí, sin embargo podrías preferir crear la tuya propia o extenderla.
La otra interfaz es para la clase de observadores (observers) y contiene un método update, que responde a las notificaciones que realiza el Subject y en el que se pasa una instancia del subject. La SPL dispone de la interfaz SplObserver.
La implementación del Subject mantiene una lista de los observadores que se suscriben a sus notificaciones. En este sentido, puede ser buena idea utilizar la clase SplObjectStorage.
El método notify, envía el mensaje update a cada uno de los observadores y estos hacen lo que les corresponda, utilizando los métodos que el Subject les permita.

Beneficios

La aplicación puede reaccionar a la ocurrencia de ciertos eventos sin que las clases de la lógica de dominio tengan que ocuparse de tareas fuera de su responsabilidad, como enviar emails, registrar en logs, generar archivos o cualquier otra.
Al estar las "reacciones" encapsuladas en un observer es fácil cambiarlas o añadir nuevas implementando y registrando nuevos observadores.

Cómo realizarlo

Hay muchas formas de implementar el patrón Observer, aunque lo que se acaba de describir es su versión más esquemática.

Diseño

Lo primero es identificar el subject y las circunstancias que nos interesa que notifique. Después tenemos que pensar en las tareas que van a realizar los observers en función del estado del Subject.

Implementación

El ejemplo de implementación del patrón Observer es un Gestor de Eventos para una aplicación realizada en CakePHP 1.3. Tiene ideas tomadas de Observer pattern: the CakePHP way, por Zen of Coding, así como de la implementación del Event Manager de CakePHP 2 y 3.
En esencia, en lugar de tener muchas clases Subject, tengo una sola (EventManager) la cual utilizan las demás clases de la aplicación cuando quieren anunciar un mensaje. Los Observers se suscriben al EventManager indicando qué mensajes quieren escuchar y con qué métodos los van a atender. Además, EventManager ignora aquellos mensajes para los que no se ha registrado ningún observador.
Crear la interfaz Subject. La interfaz subject será posteriormente implementada por la clase o clases que vayan a ser observadas. Aquí tienes un ejemplo: la clase FiSubject.
<?php
interface FiSubject
{
    public function attach(FiObserver $Observer);
    public function detach(FiObserver $Observer);
    public function notify($Generator, $message);
}

?>
Crear la interfaz Observer. La interfaz observer será la que implementen los Observers. El ejemplo que se muestra aquí (FiObserver) no sigue exactamente el patrón expuesto aquí,  ya que el método update recibe un parámetro $Generator que es el objeto que "genera" la llamada al observer y que no necesariamente va a ser el subject (forma parte de un sistema de manejo de eventos para mi aplicación).
<?php

interface FiObserver{
    /**
     * Run the method registered to proccess the message and get the message Generator
     *
     * @param string $Generator
     * @param string $message
     * @return void
     * @author Fran Iglesias
     */
    public function update($Generator, $message);
    /**
     * Return a list of messages/events supported by the observer
     *
     * @return array
     * @author Fran Iglesias
     */
    public function getEvents();
}

?>
Implementar los métodos necesarios en el Subject. La clase o clases observadas deberán implementar métodos para registrar los objetos. En el ejemplo de EventManager que muestro, se utiliza una clase SplObjectStorage para registrar los observadores (en este caso se inyecta en un método init aunque podría inyectarse perfectamente en el constructor).
<?php

App::import('Lib', 'events/FiSubject');

class EventManager implements FiSubject
{
    private $observers;
    private $messages;

    public function init(SplObjectStorage $Storage)
    {
        $this->observers =& $Storage;
        $this->messages = array();
    }

    public function attach(FiObserver $Observer)
    {
        $this->observers->attach($Observer);
        $eventsSupported = $Observer->getEvents();
        $this->messages = array_merge($this->messages, $eventsSupported);
    }

    public function detach(FiObserver $Observer)
    {
        $this->observers->detach($Observer);
    }

    public function notify($Generator, $message)
    {
        if (!in_array($message, $this->messages)) {
            return false;
        }
        foreach ($this->observers as $observer) {
             $observer->update($Generator, $message);
        }
    }
}

?>
Implementar una clase abstracta AbstractObserver. Muchas veces implemento una clase abstracta a partir de una interface para poder añadir miembros y métodos comunes en las clases finales.
En este caso, la clase Abstracta está hecha a medida para un sistema de eventos global en el que los Observers pueden registrarse para atender a ciertos mensajes, respondiendo con un método que se especifica (método listen). Por otro lado, el método update lo que hace es decidir qué método del Observer debe responder al mensaje recibido. En las clases concretas sólo tengo que implementar los métodos.
<?php

class AbstractObserver implements FiObserver
{
    private $implemented;

    public function listen($message, $method)
    {
        $this->implemented[$message] = $method;
    }

    public function update($Generator, $message)
    {
        if (!array_key_exists($message, $this->implemented)) {
            return false;
        }
        $method = $this->implemented[$message];
        if (!method_exists($this, $method)) {
            throw new BadMethodCallException(get_class().'->'.$method.' Not implemented');
        }
        $this->$method($Generator);
    }

    public function getEvents()
    {
        return array_keys($this->implemented);
    }

    public function log($message, $log = 'debug')
    {
        file_put_contents(LOGS.$log.'.log', date('Y-m-d H:i > ').$message.chr(10), FILE_APPEND);
    }

}

?>
Se puede argumentar que en este caso se rompe el principio de Responsabilidad Única pues una única clase Observer responde a diversos eventos. Esto se puede solucionar creando varios Observers que respondan cada uno a un mensaje o evento o plantear los Observers con un patrón Estrategia, con un Observer Contexto que delega en diferentes Observer Estrategias en función del mensaje recibido. Como siempre, todo depende de las necesidades específicas de la aplicación.
Implementar los Observers. En los ejemplos clásicos los observers implementan un método update que responde a los cambios del Subject. Como se comenta en el párrafo anterior, en esta implementación, update decide qué método del Observer responde a qué mensaje del sistema de eventos. He aquí un ejemplo, UserObserver.
<?php

App::import('Lib', 'events/FiObserver');
App::import('Lib', 'events/AbstractObserver');
App::import('Lib', 'fi_mailer/CakeMailer');
/**
*
*/
class UserObserver extends AbstractObserver
{
    protected $Mailer;

    public function __construct()
    {
        $this->Mailer = new CakeMailer();
    }

    public function login($User)
    {
        $data = $User->read(null);
        $this->log(sprintf(__d('access','User %s login.',true), $data['User']['realname']), 'access');
    }

    public function logout($User)
    {
        $data = $User->read(null);
        $this->log(sprintf(__d('access','User %s logout.',true), $data['User']['realname']), 'access');
    }

    public function register($User)
    {
        $data = $User->read(null);
        $this->log(sprintf(__d('access','User %s registered with account %s.',true), $data['User']['realname'], $data['User']['username']), 'access');
    }

}

?>
Registrar los Observers. Aquí puedes ver un ejemplo de un archivo events.php en el que los Observers se registran en el EventManager. Este archivo se incluye desde el bootstrap.
<?php
# Load the EventManager
App::import('Lib', 'events/EventManager');

$EventManager = new EventManager();
$EventManager->init(new SplObjectStorage());
ClassRegistry::addObject('EventManager', $EventManager);

# Init and register Observers


App::import('Lib', 'School.ApplicationObserver');
$AO = new ApplicationObserver();
$AO->useMailer($Mailer);
$AO->listen('school.application.new', 'received');
// $AO->listen('school.application.opened', 'opened');
$EventManager->attach($AO);

App::import('Lib', 'Resumes.ResumeObserver');
$RO = new ResumeObserver();
$RO->useMailer($Mailer);
$RO->listen('resumes.resume.new', 'received');
$EventManager->attach($RO);

App::import('Lib', 'Access.UserObserver');
$UO = new UserObserver();
$UO->listen('access.user.login', 'login');
$UO->listen('access.user.logout', 'logout');
$UO->listen('access.user.register', 'register');
$EventManager->attach($UO);
?>

Añadir capacidad de log a un objeto (actualizado)

Supongamos que tienes una clase o una biblioteca en un proyecto de CakePHP pero que no desciende de un objeto CakePHP y quieres que sea capaz de escribir en los archivos de log.
Pues basta con esta línea de código:


ClassRegistry::init('Object')->log('el mensaje', 'el archivo de log');

Alternativamente, puedes extender la nueva clase a partir de la clase Object de CakePHP, o de una descendiente de ella.

Alternativa mejor para añadir un log.

file_put_contents(LOGS.$log.'.log', date('Y-m-d H:i > ').$message.chr(10), FILE_APPEND);

La constante LOGS en CakePHP señala a la ubicación de los archivos de log y $log, es el archivo concreto en el que queremos escribir. $message, por supuesto, es el mensaje.

sábado, 28 de febrero de 2015

Patrón Estado (State Pattern)

Fuentes:
Patrones de comportamiento (V). Patrón State, por Daniel García
State Design Pattern, por Sourcemaking

Las cosas cambian como respuesta a diversas fuerzas, internas y externas, por ejemplo, muchas cosas cambian de estado a lo largo de un proceso en el que unas fases suceden a otras en un orden determinado.
A veces tenemos que modelar esos cambios de estado de una entidad en una aplicación. El paso de un estado a otro lo denominamos transición y es un proceso en el que pueden modificarse varias cosas en la propia entidad o en entidades relacionadas. Por ejemplo, en un sistema de gestión de biblioteca, la devolución de un libro prestado marcaría su estado como disponible, pero también retiraría el libro de la lista de préstamos pendientes de devolución de un lector y, tal vez, notificaría otro lector apuntado en una lista de espera por ese libro.
El patrón Estado (State Pattern) es útil para manejar situaciones en las que una clase puede tener diferentes estados y debe gestionar varias acciones para realizar las transiciones entre ellas. Si los cambios de estado requieren algo más que modificar el campo de estado de la clase, como podría ser aplicar ciertos cambios a otras clases o cualquier otra acción, entonces es señal de que necesitas el patrón Estado.

La situación

El patrón Estado se aplica cuando una clase tiene que gestionar estados y las correspondientes transiciones entre ellas.
Por ejemplo, un sistema de publicación (como un gestor de contenidos) en el que los artículos pueden tener estados como borrador, listo para revisar, publicado y retirado. Otro ejemplo es un sistema de incidencias de mantenimiento, en el que cada incidencia puede tener estados como abierta, asignada, en evaluación o cerrada.

El patrón

El patrón Estado encapsula los posibles estados de la entidad como clases y hace que una clase contexto delegue la realización de las transiciones en las clases de estado.
En este patrón se crea una clase de estado por cada estado que puede tomar la entidad. Cada una de estas clases implementa una interfaz que declara tantos métodos como transiciones se deban gestionar.
Cada clase implementa aquellas transiciones que tienen sentido para el estado que representan y devuelve una excepción o no hace nada si la transición no es pertinente en ese caso.
Retomando el ejemplo del sistema de publicación, desde el estado borrador podemos pasar a listo para revisar o publicar (por ejemplo si el autor tiene privilegios para publicar sin ser revisado por un editor), pero no tiene sentido pasar a retirado. Por otro lado, desde el estado publicar podríamos pasar a retirado, pero no tiene sentido pasar a listo para revisar. Evidentemente, esto depende de cómo se hayan definido las reglas de control editorial para el caso concreto, lo importante es darse cuenta de que cada clase de estado debe implementar todas las transiciones posibles en el sistema.
La clase de contexto implementará también los métodos para cada cada transición, pero en este caso lo que hará será delegar en una clase de Estado. Por supuesto, necesitará instanciar una clase Estado a partir de la propiedad de estado de la entidad que le toque manejar.

Beneficios

Este patrón elimina la necesidad de que la clase base contenga métodos con estructuras de tipo switch o múltiples if-then, que deben ser cambiados si en un momento dado necesitamos gestionar nuevos estados o modificar las operaciones que se realizan durante la transición.
Si cambian los requisitos de las transiciones modificaremos los métodos de las clases de estado. Si necesitamos gestionar nuevos estados crearemos nuevas clases de Estado.

Cómo realizarlo

Diseño

Para aplicar el patrón Estado debemos identificar primero los estados y las transiciones entre ellos. Lo más práctico es hacer un diagrama de máquina de estados en el que representamos los estados como círculos y las transiciones como flechas que nos llevan de uno a otro.
Ponemos un nombre a cada estado (normalmente un adjetivo) y un nombre a cada tipo de transición (normalmente un verbo). Estos nombres deberían indicar con claridad el estado (borrador, publicado) y la intención de la transición (publicar, retirar).

Implementación

Estoy experimentando con varias implementaciones del patrón State aunque no me ajusto estrictamente a la definición. De momento, esta es la que más me ha convencido:
Crear una interfaz State (por ejemplo, PostState) en la que declaro los métodos para las transiciones (publicar, revisar, retirar). Las clases de Estado implementarán esta interfaz, así como la clase base. Los métodos toman como parámetro una instancia de la clase base.
Crear una clase abstracta que implemente la interfaz anterior. Los métodos arrojarán una Excepción (yo he creado una StateException). Lo hago así para tener un comportamiento por defecto en las clases de estado concretas en las que, de este modo, sólo tengo que implementar los métodos necesarios para las transiciones aplicables desde un estado. (Puedes ver aquí tanto la interfaz como la clase abstracta)
Crear las clases para cada estado extendiendo la clase abstracta e implementando los métodos de las transiciones que sean adecuadas para cada estado. Como he dicho antes, la clase abstracta me proporciona un comportamiento por defecto, como puede ser arrojar una excepción o bien no hacer nada. De todos modos, estoy procurando que desde la interfaz de usuario no haya opción para realizar transiciones inválidas.
Adicionalmente, he creado una clase factoría abstracta para instanciar objetos de Estado a partir de un par de claves. Se configura pasándole tres parámetros: una clave para localizar las clases de estado, el valor de estado y el nombre de la clase que se deberá instanciar. Para obtener un objeto, se pasa la clave y el valor.
Crear o identificar una clase de contexto que contenga a la clase base y que implemente una interfaz similar a la ya definida State (un método por transición) en la que obtenga una instancia de la clase de Estado correspondiente y ejecute la transición solicitada. En mi caso he aprovechado el Controlador en el que añado las acciones correspondientes a cada una de las transiciones, que en este caso van a ser llamadas vía Ajax (aquí un ejemplo).



lunes, 28 de abril de 2014

Extra de Fixtures

Al tener una aplicación organizada en plugins puede que tengas modelos relacionados pero que residen en diferentes plugins.
Resulta un poco contradictorio tener que modificar las fixtures en un plugin para hacer tests en otro, lo que rompe la regla de aislamiento que deberían tener los tests unitarios.
Y el caso es que esto se puede evitar bastante fácilmente: tan sólo has de crear los archivos de fixtures en el plugin al que estás haciendo tests. De este modo tienes juegos de datos específicos para el plugin y no tienes que tocar para nada el otro, que quizá ya tenga sus tests completos.
Para usar estas fixtures tienes que indicarlas en el array $fixtures del TestCase. Por ejemplo, si estoy haciendo el test del modelo Item del plugin Contents y necesito fixtures del modelo Tag que está en el plugin Tags, mi array $Fixtures para ItemTestCase sería más o menos así:
var $fixtures = array(
   'plugin.contents.item',
   'plugin.contents.tag'
);
En la carpeta fixtures tendré un archivo:
tag_fixture.php
que en su caso, podría copiar de su ubucación original si ya tengo uno en el plugin Tags.