Blog


Cómo crear un listener en Symfony2 para un evento Doctrine del ciclo de vida (Lifecycle Events)

CÓMO CREAR UN LISTENER EN SYMFONY2 PARA UN EVENTO DOCTRINE DEL CICLO DE VIDA (LIFECYCLE EVENTS)

08 / 04 / 2014 Symfony

En este artículo vamos a dar un repaso a la creación de listeners en Symfony2 con eventos doctrine. Utilizaremos como ejemplo un caso en el que pretendemos que cuando se almacene la información de un nuevo Proyecto, se guarde en su campo organizacion, la del usuario que está creando el proyecto.


<?php

namespace Vabadus\AdminBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORMEntity
 * @ORMTable(name="proyecto") 
 */
class Proyecto
{
    // ...

    /** 
     * @ORMManyToOne(targetEntity="Vabadus\AdminBundle\Entity\Organizacion")
     * @ORMJoinColumn(name="organizacion_id", referencedColumnName="id", nullable=true)
     */      
    protected $organizacion;
    
    // ...
}

<?php

namespace Vabadus\AdminBundle\Entity;

use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORMEntity
 * @ORMTable(name="usuario") 
 */
class Usuario implements UserInterface
{
    // ...

    /** 
     * @ORMManyToOne(targetEntity="Vabadus\AdminBundle\Entity\Organizacion")
     * @ORMJoinColumn(name="organizacion_id", referencedColumnName="id", nullable=true)
     */      
    protected $organizacion;
    
    // ...
}

<?php

namespace Vabadus\AdminBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORMEntity
 * @ORMTable(name="organizacion") 
 */
class Organizacion
{
    /**
     * @ORMId
     * @ORMColumn(type="integer")
     * @ORMGeneratedValue
     */
    protected $id;

    /**
     * @ORMColumn(type="string", length=255))
     * @AssertNotBlank
     */
    protected $nombre;

    // ...
}

Doctrine cuenta con un completo sistema de eventos que cubre casi todos los casos que se producen en el sistema, principalmente los del ciclo de vida, siendo el EventManager el encargado de controlarlos. Por nombrar algunos de estos eventos, preRemove se produce justo antes de que el EntityManager ejecute la operación de borrado en una entidad determinada, prePersist se produce en una entidad justo antes de que el EntityManager persista en base de datos esa entidad, postUpdate es el evento que se produce después de que se haya llevado a cabo la operación de actualización de los datos de la entidad en base de datos, etc. Para ampliar información sobre los eventos del ciclo de vida de Doctrine, visita la documentación oficial.

Para conseguir el fin que nos hemos propuesto en este artículo, vamos a definir un servicio en nuestro proyecto Symfony que actúe como listener del evento prePersist de Doctrine, incluyendo además como argumento [@service_container] que nos permitirá acceder al contexto de seguridad y recuperar la organización del usuario logado y asignarla al campo correspondiente del nuevo proyecto que se está creando:


services:
  vabadus.listener.proyecto:
    class: Vabadus\AdminBundle\EventListener\ProyectoListener
    arguments: [@service_container]
    tags:
      - { name: doctrine.event_listener, event: prePersist }

A continuación, se crea la clase ProyectoListener del servicio donde se incluye la lógica necesaria para conseguir nuestro propósito. Como el servicio fue configurado como un listener del evento prePersist de Doctrine, la clase asociada a ese servicio debe contener un método prePersist, que será llamado cuando se produzca el evento. Esta clase además contará con un atributo container al que se asigna el argumento recibido por el servicio:


<?php

namespace Vabadus\AdminBundle\Event\Listener;

use Doctrine\ORMEvent\LifecycleEventArgs;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Vabadus\AdminBundle\Entity\Proyecto;

class ProyectoListener
{
    protected $container;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    public function prePersist(LifecycleEventArgs $args)
    {
        $entity = $args->getEntity();
        $entityManager = $args->getEntityManager();

        if ($entity instanceof Proyecto) {
            $usuario = $this->container->get('security.context')->getToken()->getUser();
            $entity->setOrganizacion($usuario->getOrganizacion());
        }
    }
}

Para finalizar, comentar que un listener se mantiene a la escucha de todas las entidades de nuestra aplicación, es por eso que en nuestro caso, en el que solo estábamos interesados en el manejo de una entidad específica (Proyecto), comprobamos en nuestro método que así sea: if ($entity instanceof Proyecto).



ARTÍCULOS RELACIONADOS