如何注册事件监听和订阅器

3.4 版本
维护中的版本

Doctrine封装了一个丰富的事件系统,当这个系统内部发生任何事情时,事件就会触发。对于你来说,这意味着你可以创建任意的服务并告诉Doctrine每当有某些动作(如prePersist)在Doctrine内发生时,通知这些对象。这很有用,例如,每当在您的数据库中保存一个对象时,都创建一个独立的搜索索引。

Doctrine定义了两种类型的对象,来监听Doctrine事件: listeners(监听器)和 subscribers(订阅器)。 两者都是非常相似的,但监听器还是比较简单的。有关的详细信息,请参见Doctrine官网的事件系统

Doctrine官网还解释了所有可以被监听的现存事件。

配置监听器和订阅器 

要注册一个服务作为一个事件监听器或订阅器,您只需给它标记(tag)适当的名称即可。这取决于你的用例,你可以hook(钩)一个监听器到每个DBAL连接和ORM实体管理器(ORM entity manager)里,或者只是进入一个特定的DBAL连接里,或者是所有使用此连接的实体管理器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
doctrine:
    dbal:
        default_connection: default
        connections:
            default:
                driver: pdo_sqlite
                memory: true

services:
    my.listener:
        class: AppBundle\EventListener\SearchIndexer
        tags:
            - { name: doctrine.event_listener, event: postPersist }
    my.listener2:
        class: AppBundle\EventListener\SearchIndexer2
        tags:
            - { name: doctrine.event_listener, event: postPersist, connection: default }
    my.subscriber:
        class: AppBundle\EventListener\SearchIndexerSubscriber
        tags:
            - { name: doctrine.event_subscriber, connection: default }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?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="AppBundle\EventListener\SearchIndexer">
            <tag name="doctrine.event_listener" event="postPersist" />
        </service>
        <service id="my.listener2" class="AppBundle\EventListener\SearchIndexer2">
            <tag name="doctrine.event_listener" event="postPersist" connection="default" />
        </service>
        <service id="my.subscriber" class="AppBundle\EventListener\SearchIndexerSubscriber">
            <tag name="doctrine.event_subscriber" connection="default" />
        </service>
    </services>
</container>
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
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('AppBundle\EventListener\SearchIndexer')
    )
    ->addTag('doctrine.event_listener', array('event' => 'postPersist'))
;
$container
    ->setDefinition(
        'my.listener2',
        new Definition('AppBundle\EventListener\SearchIndexer2')
    )
    ->addTag('doctrine.event_listener', array('event' => 'postPersist', 'connection' => 'default'))
;
$container
    ->setDefinition(
        'my.subscriber',
        new Definition('AppBundle\EventListener\SearchIndexerSubscriber')
    )
    ->addTag('doctrine.event_subscriber', array('connection' => 'default'))
;

创建监听类 

在前面的例子中,一个my.listener服务被配置为一个postPersist 事件的Doctrine监听器。在服务类必须有一个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();
 
        // only act on some "Product" entity
        if (!$entity instanceof Product) {
            return;
        }
 
        $entityManager = $args->getEntityManager();
        // ... do something with the Product
    }
}

在每个事件中,你可以访问一个LifecycleEventArgs 对象,通过它你可以访问事件的实体对象和实体管理器本身。

要注意的一个重要的事情是,一个监听器将监听你应用程序中的所有实体。因此,如果你有兴趣只处理一个特定类型的实体(例如,一个Product 实体但不是一个BlogPost 实体),您应该在方法中检查实体类的类型(如上所示)。

在Doctrine2.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;
// for 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();
 
        // perhaps you only want to act on some "Product" entity
        if ($entity instanceof Product) {
            $entityManager = $args->getEntityManager();
            // ... do something with the Product
        }
    }
}

Doctrine 事件订阅器不可以像 Symfony 事件订阅那样返回一系列灵活的方法来调用事件。Doctrine事件订阅器必须返回一个简单数组,里面是他们订阅的事件名称。Doctrine 期待每个订阅事件和订阅器方法使用相同的名称,就像使用事件监听器一样。

想要一个完整的参考,请看Doctrine文档的事件系统

本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。

登录symfonychina 发表评论或留下问题(我们会尽量回复)