Symfony: tutorial 13: enganchándonos a los eventos

Heart beat

Siguiendo con la puesta al día que llevo con Symfony 4 y Flex, he llegado a los eventos. En el proceso de una petición HTTP al servidor, en Symfony ocurren muchas cosas. Durante ese proceso, se van ejecutando una serie de tareas en orden. A la vez que se procesan esas tareas, se van disparando una serie de señales. Estan señales definen los eventos. Y a su vez podemos engancharnos a estas señales para personalizar el funcionamiento de la aplicación web.

Es decir, cuando un navegador hace una petición a un proyecto web Symfony, éste la recibe con el fichero public/index.php y comienza la ejecución de todo el framework junto con todo lo que hayamos construido, además de todos los componentes extra que le hayamos instalado. Ésta ejecución se hace en perfecta armonía, es una buena forma de comunicar componentes de Symfony entre sí, con los nuevos componentes que hayamos instalado, o para personalizar el comportamiento de nuestra aplicación.

Más información sobre todo esto en la documentación oficial:
https://symfony.com/doc/current/introduction/http_fundamentals.html

Listando los eventos disponibles

Sin entrar en más detalles, los podemos ver en nuestro proyecto poniendo en línea de comandos:

php bin/console debug:event-dispatcher

..con lo que veremos un listado de todos los eventos disponibles, junto con todos los “enganches”. Es decir, junto con el código que hay enganchado a estos eventos.

Pongo “enganches” porque hay que saber que este proceso se hace con los que se llaman listeners o subscribers. Es decir, haremos listeners o subscribers, para engancharnos a los eventos. Y con cada listener o subscriber tendremos cierta información para hacer lo que necesitemos.

Los eventos básicos de Symfony

Los eventos básicos de un proyecto Symfony son:

  • console.command -> se lanza justo antes de ejecutar cualquier comando de consola.
  • console.error -> se lanza cuando hay un error en comando.
  • console.terminate -> justo después de terminar un comando.
  • kernel.exception -> cuando se lanza una excepción en la ejecución de Symfony.
  • kernel.request -> justo antes de empezar a procesar la petición, antes de elegir ruta o controlador correspondiente.
  • kernel.finish_request -> cuando termina la ejecución de la petición, justo antes del controlador.
  • kernel.controller -> este evento se dispara después de saber qué controlador se va a ejecutar pero antes de que se ejecute.
  • kernel.controller_arguments -> este evento se dispara después de haber resuelto los argumentos del controlador, así se pueden tratar éstos argumentos.
  • kernel.view -> cuando ya se ha ejecutado un controlador sin devolver una respuesta se lanza este evento.
  • kernel.response -> este evento se lanza después de que una respuesta al navegador.
  • kernel.terminate -> aquí ya se ha devuelto la respuesta al navegador o cliente que ha hecho la petición.

He tratado de poner los eventos más o menos en orden. Pero hay algunos que pueden cambiar de orden, si ves alguno que no debiera estar en ese lugar deja un mensaje 😉

Entonces, ¿hacemos listeners o subscribers?

Según veo en el generador de código, sólo disponemos del siguiente comando:

php bin/console make:subscriber

Por otro lado, según leo en la documentación, Symfony internamente usa subscribers. Parece ser que los subscribers son más reutilizables, o sencillos de manejar. Así que lo más sencillo es ponerse a juguetear con un proyecto de pruebas..

Enganchándonos a un evento

Es mucho más sencillo verlo con un ejemplo que seguir escribiendo. Por ejemplo, con el componente FOSUserBundle podemos engancharnos al evento de registro de un usuario para personalizar el funcionamiento de nuestra aplicación web cuando un usuario se registra, valga la redundancia.

Simplemente he lanzado el comando de consola anterior, y me he suscrito al evento fos_user.registration.success con el fichero src/EventSubscriber/FosUserSubscriber.php siguiente:

<?php

namespace App\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use FOS\UserBundle\Event\FormEvent;
use App\Entity\Location;

class FosUserSubscriber implements EventSubscriberInterface
{
    public function onFosUserRegistrationSuccess(FormEvent $event)
    {
        $request = $event->getRequest();
        $session = $request->getSession();
        $form = $event->getForm();
        $user = $form->getData();

        //var_dump($user); exit;

        $location = new Location();
        $location->setCity($session->get('city'));
        $location->setCountryName($session->get('country_name'));
        $location->setLatitude($session->get('latitude'));
        $location->setLongitude($session->get('longitude'));

        $user->setLocation($location);
        $user->setAllowMailing(true);
        $user->setPreferredLocale($request->getLocale());
    }

    public static function getSubscribedEvents()
    {
        return [
           'fos_user.registration.success' => 'onFosUserRegistrationSuccess',
        ];
    }
}

Éste código es de un proyecto personal, que simplemente lo que que hace es guardar al usuario geolocalizado cuando éste se ha registrado. Es decir, en éste proyecto estoy localizando al usuario geográficamente, y le guardo en la sesión las variables aproximadas de su localización. Así, cuando se ha registrado, guardo junto con sus datos, su localización para saber donde está aproximadamente y así mostrarle la información más relevante a su situación geográfica.

Lo lógico es que cuando creas el subscriber, lo primero que tienes es una función vacía con un parámetro. Lo siguiente, sea cual sea el caso, es hacer un var_dump para ir tirando del hilo y viendo qué datos tenemos para personalizar el comportamiento de nuestra aplicación, o buscando en la documentación. También se puede ir al código fuente del evento en cuestión para ver qué variables tenemos disponibles..

Creando y disparando nuestros propios eventos

El siguiente paso en nivel de dificultad, sería el disparar nuestros propios eventos y dispararlos ahí donde queramos. Para esto tenemos que crear el evento, y dispararlo en algún momento de nuestra aplicación web, por otro lado. Así depués podremos engancharnos mediante un listener o subscriber a dicho evento como en el apartado anterior.

Primero necesitamos tener instalado el dispatcher para disparar los eventos, si no lo tenemos ya:

composer require symfony/event-dispatcher

Lo siguiente, una definición simple de un evento podría ser el fichero src/Event/MyProfileShowEvent.php siguiente. Es un evento que voy a disparar cuando el usuario vea su perfil:

<?php
namespace App\Event;

use Symfony\Component\EventDispatcher\Event;
use App\Entity\User;

class MyProfileShowEvent extends Event
{
    public const NAME = 'my.profile.show.event';

    protected $user;

    public function __construct(User $user)
    {
        $this->user = $user;
    }

    public function getUser()
    {
        return $this->user;
    }
}

Lo siguiente es disparar este event, en el controlador de ver el perfil. Para esto añado las siguientes líneas en el controlador:

use App\Event\MyRegistrationEvent;

..en la cabecera, y en la función de ver el perfil lo siguiente..

        // Dispatching my custom event
        $event = new MyRegistrationEvent($this->getUser());
        $this->get('event_dispatcher')->dispatch(MyRegistrationEvent::NAME, $event);

Sólo queda enganchar un subscriber de ejemplo al evento que estamo disparando. Con lo que ahora creo el subscriber para probar src/EventSubscriber/MyProfileShowSubscriber.php:

<?php

namespace App\EventSubscriber;

use App\Event\MyRegistrationEvent;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\LoggerInterface;

class MyProfileShowSubscriber implements EventSubscriberInterface
{
    public function onMyProfileShow(FormEvent $event)
    {
        // do something..
    }

    public static function getSubscribedEvents()
    {
        return [
           'my.profile.show.event' => 'onMyProfileShow',
        ];
    }
}

Sólo queda ver si Symfony detecta el enganche haciendo esto por línea de comandos:

php bin/console debug:event-dispatcher my.profile.show.event

Tenemos que ver algo parecido a la imagen siguiente:

Me remito a la documentación oficial para el que quiera seguir indagando sobre todo esto:
https://symfony.com/doc/current/components/event_dispatcher.html

Compartir..

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

diecisiete − 14 =