Zend Framework 2. Simular funcionamiento preDispatch y postDispatch de Zend Framework 1.

Zend Framework Logo Zend Framework Logo

Todo programador habituado a la versión 1 de Zend Framework se habrá encontrado en la tesitura de que al pasar a la versión 2 se han eliminado los controller plugin tal y como eran en dicha versión.

Estos plugins nos permitían agregar la ejecución de un trozo de código en momentos concretos del proceso de ejecución de la aplicación, sin importar la sección o controlador que fueramos a ejecutar.

Registrando uno o más de esos plugins conseguíamos que el propio framework llamara a determinados métodos que debíamos sobreescribir, ejecutando el código allí definido.

Dos de los métodos más usados en los controller plugin eran preDispatch y postDispatch. Estos métodos se ejecutaban justo antes de la ejecución de la acción correspondiente y justo después de la misma acción.

Como vemos es treméndamente útil para tener un punto central en el que controlar elementos comunes a todos los controladores y acciones, como por ejemplo si un usuario ha iniciado sesión o si tiene permisos para acceder a la sección a la que intenta ir.

Si esto lo hacemos en el preDispatch podremos asegurarnos de que un usuario que no ha iniciado sesión e intenta acceder a cualquier sección distinta del login, sea automáticamente dirigido a dicho formulario.

Si esto tuviéramos que repetirlo en cada controlador tendriamos una gran cantidad de código repetido, dificultando el mantenimiento de la aplicación.

El caso es que este comportamiento se ha eliminado en Zend Framework 2, sustituyéndolo por un sistema de hooks mediante manejadores de eventos, de forma que ahora la ejecución de la aplicación pasa por una serie de estados en cada uno de los cuales se dispara un evento que podremos capturar.

El primer evento que ocurre es el evento bootstrap, que es automáticamente capturado por Zend Framework llamando al método onBootstrap de las clases Module de cada módulo de la aplicación.

Ahí deberemos registrar los manejadores de eventos para los eventos posteriores. Uno de esos eventos es el evento dispatch, que es el que nosotros queremos manejar.

Un ejemplo de nuestra clase Module podría ser este:

<?php 
namespace Application; 

use Zend\Mvc\ModuleRouteListener; 
use Zend\Mvc\MvcEvent; 
use Zend\ModuleManager\Feature\ConfigProviderInterface; 
use Zend\ModuleManager\Feature\AutoloaderProviderInterface;

/**  
 * @author Calat Sistemas y Comunicaciones  
 * @link http://www.calat.com  
 */ 
class Module implements  ConfigProviderInterface, AutoloaderProviderInterface 
{          

    public function onBootstrap(MvcEvent $e) 
    {
        $eventManager        = $e->getApplication()->getEventManager();
        $moduleRouteListener = new ModuleRouteListener();
        $moduleRouteListener->attach($eventManager);

        $eventManager->getSharedManager()->attach(__NAMESPACE__, MvcEvent::EVENT_DISPATCH, function($e) {
            // This is executed on dispatch event
        });
    }

    public function getConfig()
    {
        return include __DIR__ . '/config/module.config.php';
    }

    public function getAutoloaderConfig()
    {
        return array(
            'Zend\Loader\StandardAutoloader' => array(
                'namespaces' => array(
                    __NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
                ),
            ),
        );
    }

}

El primer método es onBootstrap(), que recibe como parámetro un objeto MvcEvent. Este objeto nos permite acceder a una serie de objetos importantes en la ejecución, como por ejemplo los EventManager que usaremos para registrar más eventos.

Para añadir un manejador de eventos al evento dispatch usamos estas lineas:

$eventManager->getSharedManager()->attach(__NAMESPACE__, MvcEvent::EVENT_DISPATCH, function($e) {
    // This is executed on dispatch event
});

Esto hace lo siguiente. Usamos el objeto EventManager que hemos obtenido del objeto Application, y le agregamos (attach) un manejador de eventos al ámbito del módulo (__NAMESPACE__) sin afectar a otros módulos, sobre el evento dispatch (Usamos la constante MvcEvent::EVENT_DISPATCH, pero podríamos poner directamente ‘dispatch’) y le decimos que ejecute una función anónima (closure) que recibirá como parámetro otro objeto MvcEvent.

La característica especial es que en realidad no usamos directamente el objeto EventManager obtenido de Application, sino que desde este obtenemos el SharedEventManager, que es compartido por todos los manejadores de eventos que se instancian. Esto lo hacemos así porque en el momento en el que nos encontramos (bootstrap) no existe aún el controlador y por tanto no podemos agregar un manejador de eventos al dispatch de dicho controlador.

Si ejecutamos el código tal cual está, lo que hay dentro de la función anónima se ejecutaría con el evento dispatch, pero eso ocurre después de haber ejecutado la acción. Para que se ejecute antes imitando al preDispatch hay que añadir un último parámetro, la prioridad, que es una cifra que indica en qué orden se ejecutan los manejadores de eventos agregados a un mismo evento, y que por defecto es 1. Cuando la prioridad es igual, se ejecutan en el orden en que se han agregado.

Por tanto podríamos hacer algo así:

$eventManager->getSharedManager()->attach(__NAMESPACE__, MvcEvent::EVENT_DISPATCH, function($e) {
    // This is executed on preDispatch
}, 100);

Con esto podríamos ya controlar cosas como listas de control de acceso, usuarios logados, o cualquier acción común que tiene que ejecutarse antes de la acción.

De la misma manera, si nos queremos asegurar de que nuestro evento se ejecuta después de la acción imitando el postDispatch podemos ponerle una prioridad negativa.

$eventManager->getSharedManager()->attach(__NAMESPACE__, MvcEvent::EVENT_DISPATCH, function($e) {
    // This is executed on postDispatch
}, -100);

Y ahora si, este código imitaría el comportamiento que tenía Zend Framework version 1.

Ni que decir tiene que los eventos MVC vienen predefinidos en Zend Framework version 2, pero podríamos definir otros tipos de eventos personalizados, añadiendo así otros hooks a situaciones concretas de nuestra aplicación, lo que en el fondo lo hace más polivalente que el sistema de la versión 1.

2 Comments

  1. Helo…gracias por este articulo.

    Igual, creo que un articulo sobre definir otros tipos de eventos personalizados en zf2, estaria perfecto.

    que tal por ejemplo, interceptar x accion de usuario, registro nuevo x ejemplo….un evento podria lanzarse ahi ?

  2. Coincido con Roberto, estaría bien lo de crear eventos personalizados. Yo lo estoy intentando y por ahora no he conseguido nada.

Anímate a compartir con nosotros tus inquietudes y experiencias.

A %d blogueros les gusta esto: