Escuchas y suscriptores de eventos en Symfony2 (Doctrine)

Опубліковано: - Востаннє змінено:

Los eventos en programación web suelen hacer referencia a acciones que realiza el cliente, como por ejemplo, pulsar una tecla, seleccionar una opción de una lista o hacer click en un enlace. Sin embargo, en Symfony2 (utilizando Doctrine), el concepto de evento hace referencia a acciones que involucran a la base de datos, como una consulta, una actualización o una inserción.

¿Cómo funciona Doctrine?

Como ya sabrás si lo utilizas, Doctrine se basa un modelo ORM (Mapeo objeto-relacional), creando una interfaz entre el objeto y la base de datos. Así, podemos escuchar eventos que se generan antes o después de modificar entidades en la base de datos.

Escuchas y suscriptores de eventos en Symfony2

Doctrine define dos tipos de objetos que pueden escuchar los eventos: escuchas y suscriptores de eventos. A grandes rasgos, ambos tienen la misma funcionalidad, con alguna diferencia que comentaré más adelante. Antes de nada, veamos una situación donde nos sea de utilidad un escucha o suscriptor: Imagina que tienes una clase "Usuario" con una propiedad "Fecha de última modificación". Cada vez que un usuario actualiza sus datos (e-mail, dirección o lo que sea), esta fecha se debe actualizar con la fecha de ese momento. Este es un escenario perfecto para utilizar un escucha o suscriptor de eventos, así que voy a ilustrar este mismo ejemplo. Primero creamos la clase (o entidad) "Usuario" con la propiedad "fechaUltimaModif". Además incluiré las propiedades "id" (identificador de usuario) e "email": <?php namespace Acme\UserBundle\Entity; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints; /** * @ORM\Entity() * @ORM\Table(name = "user") */ class User { // PROPIEDADES /** * @ORM\Id * @ORM\Column(type="integer") * @ORM\GeneratedValue(strategy="AUTO") */ private $id; // Identificador único auto-generado para cada usuario /** * @ORM\Column(type="string", nullable=true) * @Constraints\Email */ private $email; // e-mail del usuario /** * @ORM\Column(type="date", nullable = true) * @Constraints\Date */ private $fechaUltimaModif; // Fecha de la última modificación de e-mail // MÉTODOS function getEmail() { return $this->email; } function setEmail($email = null) { $this->email = $email; return $this; } function getFechaUltimaModif() { return $this->fechaUltimaModif; } function setFechaUltimaModif($fechaUltimaModif = null) { $this->fechaUltimaModif = $fechaUltimaModif; return $this; } }

Crear la acción de cambiar e-mail desde el controlador

Ahora que tenemos la entidad "Usuario", vamos a crear la acción de cambiar e-mail desde el controlador. Voy a suponer que tenemos un método getUser() para obtener el usuario que está cambiando el e-mail: <?php namespace Acme\UserBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Acme\UserBundle\Entity\User; use Symfony\Component\Routing\Annotation\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Component\HttpFoundation\Request; class DefaultController extends Controller { /** * @Route("editar-email", name="edit_email") * @Template */ function emailEditAction(Request $request) { $user = $this->getUser(); // nuestra propia función para obtener el usuario. //Formulario para cambiar e-mail $form = $this->createFormBuilder($user) ->add('email', 'email', array(     'label' => "e-mail",     'data' => $user->getEmail() ))->getForm(); if ($this->getRequest()->isMethod('post') && $form->bind($request)->isValid()) { $this->getDoctrine()->getManager()->persist($user); $this->getDoctrine()->getManager()->flush(); return $this->redirect($this->getRequest()->getUri()); } return array('form' => $form->createView()); } }

Plantilla para generar la vista

También necesitaremos una plantilla para generar la vista. Dicha plantilla será el formulario para cambiar el e-mail. Para simplificar no añadiré un botón de aceptar. Simplemente pulsaremos "enter" para enviar el formulario. src/Acme/UserBundle/Resources/views/Default/emailEdit.html.twig <form method=post id="form_id"> {{ form_widget(form) }} </form>

Ya podemos modificar el email

Ahora ya tenemos el escenario que buscábamos. Si accedemos a dominio/app_dev.php/editar-email veremos un campo donde modificar el e-mail. Si modificamos este campo y pulsamos "enter", el e-mail antiguo (si existe) se sobrescribirá en la base de datos con el nuevo valor introducido. Como comentaba al principio, al modificar un usuario su e-mail, queremos que se actualice el valor "fechaUltimaModif" en la base de datos con el valor de fecha actual. Seguramente estarás pensando que podrías simplemente modificar este campo desde el controlador, una vez modificado el e-mail. Sin embargo, es posible que el e-mail sea modificado desde otras plantillas, o incluso desde otro controlador u otro paquete (Bundle).

Realizar la acción independientemente de donde se modifique el e-mail

Por este motivo es interesante poder realizar esta acción independientemente de donde se modifique el e-mail. Aquí es donde entran las escuchas o suscriptores de eventos. Un suscriptor o escucha estará atento a la modificación de entidades, antes y después de persistirlos en la base de datos. Los escuchas y suscriptores se configuran como servicios, así que modificaremos el archivo app/config/services.yml y añadiremos las siguientes líneas: services: mi.escuchador: class: Acme\UserBundle\EventListener\emailEditListener tags: - { name: doctrine.event_listener, event: preUpdate }   Finalmente, definiremos la clase emailEditListener en Acme\UserBundle\EventListener\emailEditListener.php: <?php namespace Acme\UserBundle\EventListener; use Doctrine\ORM\Event\LifecycleEventArgs; use Acme\UserBundle\Entity\User; class emailEditListener { function preUpdate(LifecycleEventArgs $args) { $entity = $args->getEntity(); if ($entity instanceof User) { //Cambiamos la fecha de última modificación $entity->setFechaUltimaModif(new \Datetime); } } } El ejemplo anterior es un escucha de eventos, en concreto del evento "preUpdate", que se dispara cada vez que una entidad es modificada (no tiene en cuenta nuevas entidades). Existe una lista de los eventos disponibles en esta página. Como se puede ver en el código, es necesario comprobar si la entidad que se está persistiendo es la entidad "Usuario", ya que el evento será disparado con cada persistencia de cualquier entidad.

Modificar la fecha

¿Cómo lo haríamos si queremos modificar la fecha también cuando una nueva entidad usuario es creada? Podríamos suscribirnos a varios eventos (en este caso "preUpdate" y "prePersist") mediante un suscriptor de eventos. Así, tendríamos: services: mi.suscriptor: class: Acme\UserBundle\EventListener\emailEditSubscriber tags: - { name: doctrine.event_subscriber, connection: default } <?php namespace Acme\UserBundle\EventListener; use Doctrine\ORM\Event\LifecycleEventArgs; use Doctrine\Common\EventSubscriber; use Acme\UserBundle\Entity\User; class emailEditSubscriber implements EventSubscriber { function getSubscribedEvents(LifecycleEventArgs $args) { return array( 'preUpdate', 'prePersist' ); } public function preUpdate(LifecycleEventArgs $args) { $this->index($args); } public function prePersist(LifecycleEventArgs $args) { $this->index($args); } public function index(LifecycleEventArgs $args) { $entity = $args->getEntity(); // para actuar en la entidad "Usuario" if ($entity instanceof User) { //Cambiamos la fecha de última modificación $entity->setFechaUltimaModif(new \Datetime); } } } Por último, recordar que además de poder acceder a la entidad ($entity = $args->getEntity();), es también posible acceder a la clase "EntityManager" mediante $entityManager = $args->getEntityManager();, tanto en el escuchador, como en el suscriptor de eventos. banner-symfony    

Наступна стаття

E-commerce: los 5 expertos que necesitas para lanzar tu tienda online