Laravel 事件系统

Laravel框架
549
0
0
2022-04-18
标签   Laravel基础

Laravel 事件系统

Dispatcher 是一个观察者模式的实现,它最大的优点就是在对象之间没有依赖关系的情况下,把对象内部的状态暴露给对该状态感兴趣的对象,

通常我们把这类对象称为监听器,监听器的本质是一个可执行的函数;状态称为事件,通常事件都有一个唯一的名称,和携带一个可选的状态数据。

通过下面的例子可以体验下事件系统的优点。

class Page
{
    protected $dispatcher;

    public function __construct(Dispatcher $dispatcher)
    {
        $this->dispatcher = $dispatcher;
    }


    public function render()
    {
        $this->dispatcher->dispatch('css.loaded');

        $this->dispatcher->dispatch('javascript.loaded');

        $this->dispatcher->dispatch('html.painting');

        $this->dispatcher->dispatch('html.painted');
    }
}

$dispatcher = new Dispatcher();
$dispatcher->listen('css.loaded', function () {
    echo "css loaded ok \n";
});

$dispatcher->listen('javascript.loaded', function () {
    echo "javascript loaded ok \n";
});

$dispatcher->listen('html.painting', function () {
    echo "ready for  painting\n";
});
$dispatcher->listen('html.painted', function () {
    echo "render ok \n";
});


$page = new Page($dispatcher);
$page->render();

在写 render 方法不用考虑对外的依赖关系,只需要把相应的事件派发就好,这些事件派发的点,本质就是一个功能延申扩展的切入点。

这种用法在 laravel 很多地方都有使用,例如每个 http 请求被处理完成之后都有派发一个 new RequestHandled($request, $response)

这个事件表示请求已经被处理,如果你想在请求被处理完成之后,做一个额外的操作–比如记录日志,就可以监听该事件。

基本使用

通常 Dispatcher 会作为一个全局的单例对象被使用,在系统初始化的事件设置监听器,然后在具体业务逻辑中派发相应的事件。

定义事件

事件通常有两部分组成

  • 事件的唯一名称,为了简单,如果事件是一个类,可以使用类的全名称作为事件的唯一标记;
  • 事件携带的数据,这部分是可选;

RequestHandled 就是一个事件,它的事件名称为Illuminate\Foundation\Http\Events\RequestHandled

它包含了请求对象和相应对象两个数据属性。

class RequestHandled
{
    /**
     * The request instance.
     *
     * @var \Illuminate\Http\Request
     */ 
    public $request;

    /**
     * The response instance.
     *
     * @var \Illuminate\Http\Response
     */ 
    public $response;

    /**
     * Create a new event instance.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Illuminate\Http\Response  $response
     * @return void
     */ 
    public function __construct($request, $response)
    {
        $this->request = $request;
        $this->response = $response;
    }
}

事件监听器设置

使用 public function listen($events, $listener = null) 设置监听器,通常有以下4种用法

  • $events 为事件的唯一名称,$listener 为一个函数
  • $events 为一个函数,但函数的第一个参数为事件的唯一名称,$listener 为空
  • $events 为事件的唯一名称 $listener 为一个监听器类的名称,但是该类必须有 handle 方法或者 __invoke
  • $events为事件的唯一名称$listener` 为一个数组,数组的第一个元素为监听器类名称,第二个参数为处理事件的方法名称
// 方式一 函数监听器
$dispatcher->listen(RequestHandled::class,function (RequestHandled  $event){
    echo sprintf("请求%s已经被处理,相应的状态码为%d\n",$event->request->url(),$event->response->status());
});


// 方式二 省略事件名称
$dispatcher->listen(function (RequestHandled  $event){
    echo sprintf("请求%s已经被处理,相应的状态码为%d\n",$event->request->url(),$event->response->status());
});


//方式三:使用类监听器,默认是使用监听器的`handle`方法处理,如果找不到则使用`__invoke`方法,否则处抛出异常
class RequestHandledListener{
    public function handle($event){
        echo sprintf("请求%s已经被处理,相应的状态码为%d\n",$event->request->url(),$event->response->status());
    }
}
$dispatcher->listen(RequestHandled::class,RequestHandledListener::class);

// 方式四:指定监听器处理的方法
class RequestHandledListenerMethod{
    public function hand2($event){
        echo sprintf("请求%s已经被处理,相应的状态码为%d\n",$event->request->url(),$event->response->status());
    }
}

$dispatcher->listen(RequestHandled::class,[RequestHandledListenerMethod::class,'hand2']);

事件订阅器

事件订阅器的是一个带有 subscribe 方法的类,subscribe 方法内部实现一些列的监听器的设置。

下面例子的本质是把用户相关的事件监听器的设置封装到一个名为 UserEventSubscriber 中

class UserEventSubscriber
{
    /**
     * Handle user login events.
     */ 
    public function handleUserLogin($event) {}

    /**
     * Handle user logout events.
     */ 
    public function handleUserLogout($event) {}

    /**
     * Register the listeners for the subscriber.
     *
     * @param  \Illuminate\Events\Dispatcher  $events
     * @return void
     */ 
    public function subscribe($events)
    {
        $events->listen(
            'Illuminate\Auth\Events\Login',
            [UserEventSubscriber::class, 'handleUserLogin']
        );

        $events->listen(
            'Illuminate\Auth\Events\Logout',
            [UserEventSubscriber::class, 'handleUserLogout']
        );
    }
}


$dispatcher->subscribe(UserEventSubscriber::class); // 设置订阅器