Cómo registrar escuchas y suscriptores de eventos

Doctrine cuenta con un rico sistema de eventos que lanza eventos en casi todo lo que sucede dentro del sistema. Para ti, esto significa que puedes crear servicios arbitrarios y decirle a Doctrine que notifique a esos objetos cada vez que ocurra una determinada acción (por ejemplo, PrePersist) dentro de Doctrine. Esto podría ser útil, por ejemplo, para crear un índice de búsqueda independiente cuando se guarde un objeto en tu base de datos.

Doctrine define dos tipos de objetos que pueden escuchar los eventos de Doctrine: escuchas y suscriptores. Ambos son muy similares, pero los escuchas son un poco más sencillos. Para más información, consulta el Sistema de eventos en el sitio web de Doctrine.

The Doctrine website also explains all existing events that can be listened to.

Configurando escuchas/suscriptores

Para registrar un servicio para que actúe como un escucha o suscriptor de eventos sólo lo tienes que etiquetar con el nombre apropiado. Dependiendo de tu caso de uso, puedes enganchar un escucha en cada conexión DBAL y gestor de entidad ORM o simplemente en una conexión DBAL específica y todos los gestores de entidad que utilicen esta conexión.

  • YAML
    doctrine:
        dbal:
            default_connection: default
            connections:
                default:
                    driver: pdo_sqlite
                    memory: true
    
    services:
        my.listener:
            class: Acme\SearchBundle\EventListener\SearchIndexer
            tags:
                    - { name: doctrine.event_listener, event: postPersist }
        my.listener2:
            class: Acme\SearchBundle\EventListener\SearchIndexer2
            tags:
                    - { name: doctrine.event_listener, event: postPersist, connection: default }
        my.subscriber:
            class: Acme\SearchBundle\EventListener\SearchIndexerSubscriber
            tags:
                    - { name: doctrine.event_subscriber, connection: default }
    
  • XML
    <?xml version="1.0" ?>
    <container xmlns="http://symfony.com/schema/dic/services"
        xmlns:doctrine="http://symfony.com/schema/dic/doctrine">
    
        <doctrine:config>
            <doctrine:dbal default-connection="default">
                <doctrine:connection driver="pdo_sqlite" memory="true" />
            </doctrine:dbal>
        </doctrine:config>
    
            <services>
            <service id="my.listener" class="Acme\SearchBundle\EventListener\SearchIndexer">
                <tag name="doctrine.event_listener" event="postPersist" />
            </service>
            <service id="my.listener2" class="Acme\SearchBundle\EventListener\SearchIndexer2">
                <tag name="doctrine.event_listener" event="postPersist" connection="default" />
            </service>
            <service id="my.subscriber" class="Acme\SearchBundle\EventListener\SearchIndexerSubscriber">
                <tag name="doctrine.event_subscriber" connection="default" />
            </service>
            </services>
    </container>
    
  • PHP
    use Symfony\Component\DependencyInjection\Definition;
    
    $container->loadFromExtension('doctrine', array(
        'dbal' => array(
            'default_connection' => 'default',
            'connections' => array(
                'default' => array(
                    'driver' => 'pdo_sqlite',
                    'memory' => true,
                ),
            ),
        ),
    ));
    
    $container
        ->setDefinition(
            'my.listener',
            new Definition('Acme\SearchBundle\EventListener\SearchIndexer')
        )
        ->addTag('doctrine.event_listener', array('event' => 'postPersist'))
    ;
    $container
        ->setDefinition(
            'my.listener2',
            new Definition('Acme\SearchBundle\EventListener\SearchIndexer2')
        )
        ->addTag('doctrine.event_listener', array('event' => 'postPersist', 'connection' => 'default'))
    ;
    $container
        ->setDefinition(
            'my.subscriber',
            new Definition('Acme\SearchBundle\EventListener\SearchIndexerSubscriber')
        )
        ->addTag('doctrine.event_subscriber', array('connection' => 'default'))
    ;
    

Creando la clase Escucha

En el ejemplo anterior, se configuró un servicio my.listener como un escucha de Doctrine del evento postPersist. The class behind that service must have a postPersist method, which will be called when the event is dispatched:

// src/Acme/SearchBundle/EventListener/SearchIndexer.php
namespace Acme\SearchBundle\EventListener;

use Doctrine\ORM\Event\LifecycleEventArgs;
use Acme\StoreBundle\Entity\Product;

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

        // tal vez sólo quieres actuar en alguna entidad "producto"
        if ($entity instanceof Product) {
            // ... haz algo con el Producto
        }
    }
}

En cada caso, tienes acceso a un objeto LifecycleEventArgs, el cual te da acceso tanto al objeto entidad del evento como al mismo gestor de la entidad.

Una cosa importante a resaltar es que un escucha debe estar atento a todas las entidades en tu aplicación. So, if you’re interested in only handling a specific type of entity (e.g. a Product entity but not a BlogPost entity), you should check for the entity’s class type in your method (as shown above).

Creating the Subscriber Class

A doctrine event subscriber must implement the Doctrine\Common\EventSubscriber interface and have an event method for each event it subscribes to:

// src/Acme/SearchBundle/EventListener/SearchIndexerSubscriber.php
namespace Acme\SearchBundle\EventListener;

use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\LifecycleEventArgs;
// for doctrine 2.4: Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use Acme\StoreBundle\Entity\Product;

class SearchIndexerSubscriber implements EventSubscriber
{
    public function getSubscribedEvents()
    {
        return array(
            'postPersist',
            'postUpdate',
        );
    }

    public function postUpdate(LifecycleEventArgs $args)
    {
        $this->index($args);
    }

    public function postPersist(LifecycleEventArgs $args)
    {
        $this->index($args);
    }

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

        // tal vez sólo quieres actuar en alguna entidad "producto"
        if ($entity instanceof Product) {
            // ... haz algo con el Producto
        }
    }
}

Truco

Doctrine event subscribers can not return a flexible array of methods to call for the events like the Symfony event subscriber can. Doctrine event subscribers must return a simple array of the event names they subscribe to. Doctrine will then expect methods on the subscriber with the same name as each subscribed event, just as when using an event listener.

For a full reference, see chapter `The Event System`_ in the Doctrine documentation.

Bifúrcame en GitHub