Как зарегистрировать слушателей событий и подписчиков

Как зарегистрировать слушателей событий и подписчиков

Пакеты Doctrine имеют богатую систему событий, которая выдаёт события почти всегда, когда в системе что-либо происходит. Для вас, это значит, что вы можете создавать произвольные сервисы и говорить Doctrine уведомлять эти объекты, когда с Doctrine происходит некоторое действие (например, prePersist()). Это будет полезным, например, чтобы создать независимый поисковый индекс при сохранении объекта в вашу базу данных.

Doctrine определяет два типа объектов, которые могут слушать события Doctrine: слушатели и подписчики. Оба типа схожи, но слушатели слегка более прямые. Чтобы узнать больше, смотрите Система событий на сайте Doctrine.

Веб-страница Doctrine также разъясняет все существующие события, которые можно слушать.

Конфигурация слушателя/подписчика

Чтобы зарегистрировать сервис так, чтобы он вёл себя как слушатель событий или подписчик, вам нужно всего-лишь тегировать его с помощью правильного имени. В зависимости от вашего примера использования, вы можете подключить слушателя к каждому соединению DBAL и менеджеру сущностей ORM, или только к одному конкретному соединению DBAL и ко всем менеджерам сущностей, которые это соединение используют.

  • YAML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    services:
        # ...
    
        AppBundle\EventListener\SearchIndexer:
            tags:
                - { name: doctrine.event_listener, event: postPersist }
        AppBundle\EventListener\SearchIndexer2:
            tags:
                - { name: doctrine.event_listener, event: postPersist, connection: default }
        AppBundle\EventListener\SearchIndexerSubscriber:
            tags:
                - { name: doctrine.event_subscriber, connection: default }
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    <?xml version="1.0" ?>
    <container xmlns="http://symfony.com/schema/dic/services"
        xmlns:doctrine="http://symfony.com/schema/dic/doctrine">
        <services>
            <!-- ... -->
    
            <service id="AppBundle\EventListener\SearchIndexer">
                <tag name="doctrine.event_listener" event="postPersist" />
            </service>
            <service id="AppBundle\EventListener\SearchIndexer2">
                <tag name="doctrine.event_listener" event="postPersist" connection="default" />
            </service>
            <service id="AppBundle\EventListener\SearchIndexerSubscriber">
                <tag name="doctrine.event_subscriber" connection="default" />
            </service>
        </services>
    </container>
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    use AppBundle\EventListener\SearchIndexer;
    use AppBundle\EventListener\SearchIndexer2;
    use AppBundle\EventListener\SearchIndexerSubscriber;
    
    $container->autowire(SearchIndexer::class)
        ->addTag('doctrine.event_listener', array('event' => 'postPersist'))
    ;
    $container->autowire(SearchIndexer2::class)
        ->addTag('doctrine.event_listener', array(
            'event' => 'postPersist',
            'connection' => 'default'
        ))
    ;
    $container->autowire(SearchIndexerSubscriber::class)
        ->addTag('doctrine.event_subscriber', array('connection' => 'default'))
    ;
    

Создание класса слушателя

В предыдущем примере, сервис SearchIndexer был сконфигуррован как Doctrine слушатель события postPersist. Класс, стоящий за этим сервисом, должен иметь метод postPersist(), который будет вызван при осуществлении события:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// src/AppBundle/EventListener/SearchIndexer.php
namespace AppBundle\EventListener;

use Doctrine\ORM\Event\LifecycleEventArgs;
use AppBundle\Entity\Product;

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

        // действовать только на сущность "Product"
        if (!$entity instanceof Product) {
            return;
        }

        $entityManager = $args->getEntityManager();
        // ... сделать что-то с Product
    }
}

В каждом событии, у вас есть доступ к объекту LifecycleEventArgs, который даёт вам доступ и к объекту сущности события, и к самому менеджеру.

Важно заметить одну вещь: слушатель будет слушать все сущности в вашем приложении. Так что если вы заинтересованы только в каком-то конкретном типе сущностей (например, сушности Product, но не сущности``BlogPost``), вам нужно проверить тип класса сущности в вашем методе (как показано выше).

Tip

В версии Doctrine 2.4, была представлена функция под названием "Слушатели сущностей". Это класс слушателя жизненного цикла, используемый для сущности. Вы можете прочитать о нём в `Документации Doctrine`_.

Создание класса подписчика

Подписчик событий Doctrine должен внедрять интерфейс Doctrine\Common\EventSubscriber и иметь метод события для каждого события, на которое он подписывается:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// src/AppBundle/EventListener/SearchIndexerSubscriber.php
namespace AppBundle\EventListener;

use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\LifecycleEventArgs;
// для Doctrine 2.4: Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use AppBundle\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();

        // возможно, вы хотите действовать только на некой сущности "Product"
        if ($entity instanceof Product) {
            $entityManager = $args->getEntityManager();
            // ... сделать что-то с Product
        }
    }
}

Tip

Подписчики событий Doctrine не могут возвращать гибкий массив методов для вызова событий, как это может сделать подписчик событий Symfony. Подписчики событий Doctrine должны возвращать простой массив имён событий, на которые они подписываются. После этого Doctrine будет ожидать методов подписчиков с такими же именами, как каждое событие, на которое была оформлена подписка, так же, как и при использовании слушателя событий.

Для полной справки, смотрите главу Система событий в документации Doctrine.

Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.