理解 Doctrine 2 事件转载

March 04, 2020
-
238
Doctrine Event 事件是指在Doctrine 执行中为执行某些额外动作,定义的触发方式。如果系统逻辑稍微复杂后,那么通过Doctrine事件功能对逻辑进行解耦会是非常必要的。

什么是Doctrine Event?

Doctrine Event 事件是指在Doctrine 执行中为执行某些额外动作,定义的触发方式。

例如,当用户注册帐号后,系统向他发送注册成功邮件。 实现这个业务最简单的方法是在注册成功后增加一段逻辑代码去发送邮件,这样做的话两个业务被混合在一起,如果修改密码后也要发送邮件,那只能修改用户修改密码的业务逻辑实现。现在通过Doctrine事件功能可以非常轻松的解耦代码,发送邮件的逻辑调整不需要改动用户部分业务代码。

如果系统逻辑稍微复杂后,那么通过Doctrine事件功能对逻辑进行解耦会是非常必要的。

Doctrine Event 事件类型


  • preRemove – 在执行给定实体的相应EntityManager删除操作之前,先执行preRemove事件。DQL DELETE语句不调用它。

  • postRemove – 删除实体后,执行postRemove事件。数据库删除操作后将调用它。DQL DELETE语句不调用它。

  • prePersist – 在执行给定实体的相应EntityManager持久化操作之前,先执行prePersist事件。应当注意,此事件仅在实体的初始持久存在时触发(即,它不会在将来的更新时触发)。

  • postPersist – 实体持久化后,实体将发生postPersist事件。数据库插入操作后将调用它。生成的主键值在postPersist事件中可用。

  • preUpdate – 对实体数据进行数据库更新操作之前执行。DQL UPDATE语句不调用它。

  • postUpdate – 在数据库对Entity数据进行更新操作之后,将发生postUpdate事件。DQL UPDATE语句不调用它。

  • postLoad – 在Entity从数据库加载到当前的EntityManager中或对其执行刷新操作之后,该实体就会发生postLoad事件。

  • loadClassMetadata – 在从映射源(annotations / xml / yaml)加载了类的映射元数据之后,执行loadClassMetadata事件。此事件不是生命周期回调。

  • preFlush – preFlush事件在Flush操作开始时发生。此事件不是生命周期回调。

  • onFlush –计算所有管理实体的change-sets之后,将执行onFlush事件。此事件不是生命周期回调。

  • postFlush – postFlush事件在Flush操作结束时发生。此事件不是生命周期回调。

  • onClear –在从工作单元中删除了对实体的所有引用之后,调用EntityManager#clear()操作时将执行onClear事件。此事件不是生命周期回调。

有四种类型的事件:


  • Lifecycle callbacks,定义在实体类(Entity 或 Orm)上的方法,并在事件触发时被调用;

  • Lifecycle listeners 和 subscribers,监听事件和订阅事件是带有一个或多个事件的回调方法的类,并且为所有实体调用;

  • Entity listeners,类似于 Lifecycle callbacks,但仅针对特定类的实体调用它们。

缺点和优点:


  • Lifecycle callbacks具有更好的性能,因为它们仅适用于单个实体类,但是您不能为不同的实体重用逻辑,并且它们无权访问Symfony服务;

  • Lifecycle listeners 和 subscribers 可以在不同实体之间重用逻辑,并且可以访问Symfony服务,但是它们的性能较差,因为它们被所有实体调用。

  • Entity listeners 与生命周期侦听器具有相同的优点,并且它们具有更好的性能,因为它们仅适用于单个实体类。

Lifecycle Callbacks(生命周期回调)

示例

YAML
# config/doctrine/Product.orm.yml
App\Entity\Product:
type: entity
# ...
lifecycleCallbacks:
prePersist: ['setCreatedAtValue']

Entity

// src/Entity/Product.php
use Doctrine\ORM\Mapping as ORM;
class Product
{
// ...
/**
* @ORM\PrePersist
*/
public function setCreatedAtValue()
{
$this->createdAt = new \DateTime();
}
}


Lifecycle Listeners(生命周期监听事件)

YAML

# config/services.yaml

services:

# ...

App\EventListener\ProductChange:

tags:

-

name: 'doctrine.event_listener'

# this is the only required option for the lifecycle listener tag

event: 'postPersist'



# listeners can define their priority in case multiple listeners are associated

# to the same event (default priority = 0; higher numbers = listener is run earlier)

priority: 500



# you can also restrict listeners to a specific Doctrine connection

connection: 'default'


PHP 监听事件

// src/EventListener/ProductChange

namespace App\EventListener;

use App\Entity\Product;

use Doctrine\Persistence\Event\LifecycleEventArgs;

class ProductChange

{

// the listener methods receive an argument which gives you access to

// both the entity object of the event and the entity manager itself

public function postPersist(LifecycleEventArgs $args)

{

$entity = $args->getObject();



// if this listener only applies to certain entity types,

// add some code to check the entity type as early as possible

if (!$entity instanceof Product) {

return;

}



$entityManager = $args->getObjectManager();

// ... do something with the Product entity

}

}


Entity Listeners(对象实体监听事件)

YAML

# config/services.yaml

services:

# ...

App\EventListener\ProductChange:

tags:

-

# these are the basic options that define the entity listener

name: 'doctrine.orm.entity_listener'

event: 'postUpdate'

entity: 'App\Entity\Product'



# set the 'lazy' option to TRUE to only instantiate listeners when they are used

lazy: true



# you can also associate an entity listener to a specific entity manager

entity_manager: 'custom'



# by default, Symfony looks for a method called after the event (e.g. postUpdate())

# if it doesn't exist, it tries to execute the '__invoke()' method, but you can

# configure a custom method name with the 'method' option

method: 'checkUserChanges'


Event Listener

// src/EventListener/ProductChange.php

namespace App\EventListener;

use App\Entity\Product;

use Doctrine\Persistence\Event\LifecycleEventArgs;

class ProductChange

{

// the entity listener methods receive two arguments:

// the entity instance and the lifecycle event

public function postUpdate(Product $product, LifecycleEventArgs $event)

{

// ... do something to notify the changes

}

}


Lifecycle Subscribers(生命周期订阅事件)

YAML

# config/services.yaml

services:

# ...

App\EventListener\ProductChangeSubscriber:

tags:

- { name: 'doctrine.event_subscriber', connection: 'default' }


Event Subscribers

namespace App\EventListener;

use App\Entity\Product;

use Doctrine\Common\EventSubscriber;

use Doctrine\ORM\Events;

use Doctrine\Persistence\Event\LifecycleEventArgs;

class ProductChangeSubscriber implements EventSubscriber

{

// this method can only return the event names; you cannot define a

// custom method name to execute when each event triggers

public function getSubscribedEvents()

{

return [

Events::postPersist,

Events::postRemove,

Events::postUpdate,

];

}



// callback methods must be called exactly like the events they listen to;

// they receive an argument of type LifecycleEventArgs, which gives you access

// to both the entity object of the event and the entity manager itself

public function postPersist(LifecycleEventArgs $args)

{

$this->logActivity('persist', $args);

}



public function postRemove(LifecycleEventArgs $args)

{

$this->logActivity('remove', $args);

}



public function postUpdate(LifecycleEventArgs $args)

{

$this->logActivity('update', $args);

}



private function logActivity(string $action, LifecycleEventArgs $args)

{

$entity = $args->getObject();



// if this subscriber only applies to certain entity types,

// add some code to check the entity type as early as possible

if (!$entity instanceof Product) {

return;

}


// ... get the entity information and log it somehow

}

}


参考文章:

https://symfony.com/doc/current/doctrine/events.html#doctrine-lifecycle-callbacks

https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/events.html#lifecycle-callbacks-event-argument



https://culttt.com/2014/08/04/understanding-doctrine-2-lifecycle-events/