Laravel中的事件与监听,观察者模式

Laravel中的事件与监听使用的了观察者模式,观察者模式可以做到优雅的处理一连串的动作,动态的增加和减少动作,而不用去改变主线业务代码。

简介

Laravel 的事件提供了一个简单的观察者实现,能够订阅和监听应用中发生的各种事件。事件类通常存放在 app/Events 目录中,而这些事件类的监听器则存放在 app/Listeners 中。如果你没有在你的应用中看到这些目录,别担心,它们会在你使用 Artisan 控制台命令生成事件与监听器的时候自动创建。

事件系统为应用各个方面的解耦提供了非常棒的方法,因为单个事件可以拥有多个互不依赖的监听器。举个例子,你可能希望每次订单发货时向用户发送一个 Slack 通知。你可以简单地发起一个可以被监听器接收并转化为 Slack 通知的 OrderShipped 事件,而不是将订单处理代码和 Slack 通知代码耦合在一起。

观察者模式:https://blog.csdn.net/raoxiaoya/article/details/92801041

注册事件 & 监听器

Laravel 应用中的 app/Providers/EventServiceProvider 为注册所有的事件监听器提供了一个便利的场所。其中, listen 属性包含了所有事件(键)以及事件对应的监听器(值)的数组。当然,你可以根据应用的需要,添加多个事件到 listen 属性包含的数组中。

<?php

namespace App\Providers;

use Illuminate\Support\Facades\Event;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

class EventServiceProvider extends ServiceProvider
{
    protected $listen = [
        Registered::class => [
            SendEmailVerificationNotification::class,
        ],
    ];

    public function boot()
    {
        parent::boot();
    }
}

我们在$listen中添加自己的事件与监听,此时,我们还没有创建相应的类文件,因此我们只能使用字符串的形式来定义:

protected $listen = [
    Registered::class => [
        SendEmailVerificationNotification::class,
    ],
    'App\Events\TestEvent'=>[
        'App\Listeners\TestListenerOne',
        'App\Listeners\TestListenerTwo',
    ],
];

然后使用命令批量生成文件

php artisan event:generate

于是我们生成的文件如下:

<?php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class TestEvent
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    /**
     * Create a new event instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return new PrivateChannel('channel-name');
    }
}

<?php

namespace App\Listeners;

use App\Events\TestEvent;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

class TestListenerOne
{
    /**
     * Create the event listener.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     *
     * @param  TestEvent  $event
     * @return void
     */
    public function handle(TestEvent $event)
    {
        //
    }
}

可见会自动为监听器的 handle 方法注入事件的类。
已经存在的就不会再次创建。

事件订阅者

上面这种注册监听器的方式,一个事件对应多个监听器类文件,会出现监听器文件过多的情况,同时,如果多个事件要使用同一个监听器,会出现冲突,因为一个监听器只能注入一个监听器,当然你也可以复制它然后换个名字,但是这样做不合理,不利于维护。此时,我们可以使用事件订阅者来简化。
事件订阅者是可以在自身内部订阅多个事件的类,即能够在单个类中定义多个事件处理器。订阅者应该定义一个 subscribe 方法,这个方法接收一个事件分发器实例。你可以调用给定事件分发器上的 listen 方法来注册事件监听器:

<?php

namespace App\Listeners;

class UserEventSubscriber
{
    /**
     * 处理用户登录事件。
     */
    public function onUserLogin($event) {}

    /**
     * 处理用户注销事件。
     */
    public function onUserLogout($event) {}

    /**
     * 为订阅者注册监听器。
     *
     * @param  \Illuminate\Events\Dispatcher  $events
     */
    public function subscribe($events)
    {
        $events->listen(
            'Illuminate\Auth\Events\Login',
            'App\Listeners\UserEventSubscriber@onUserLogin'
        );

        $events->listen(
            'Illuminate\Auth\Events\Logout',
            'App\Listeners\UserEventSubscriber@onUserLogout'
        );
    }

}

然后需要注册这个事件订阅者,你可以在 EventServiceProvider 中的 $subscribe 属性中注册订阅者。

<?php

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

class EventServiceProvider extends ServiceProvider
{
    /**
     * 应用中事件监听器的映射。
     *
     * @var array
     */
    protected $listen = [
        //
    ];

    /**
     * 需要注册的订阅者类。
     *
     * @var array
     */
    protected $subscribe = [
        'App\Listeners\UserEventSubscriber',
    ];
}

显然,订阅者的方式更啊级方便,但是具有一点耦合。

停止事件往下传递

有时候,当一个事件执行失败了,或者没必要继续往下执行的时候,可以在 handle 方法中 return false 来终止。

事件监听器队列

如果你的监听器中要执行诸如发送电子邮件或者进行 HTTP 请求等比较慢的任务,你可以选择将其丢给队列处理。在开始使用队列监听器之前,请确保在你的服务器或者本地开发环境中能够 配置队列 并启动一个队列监听器。要指定监听器启动队列,只要让监听器实现 ShouldQueue 接口。实际上,由 Artisan 命令 event:generate 生成的监听器已经将此接口导入到当前命名空间中,因此可以直接使用:

class TestListenerOne implements ShouldQueue

就是这个!当这个监听器被事件调用时,事件调度器会自动使用 Laravel 的队列系统。如果在队列中执行监听器时没有抛出异常,任务会在执行完成后自动从队列中删除。

如果你想要自定义事件监听器所使用的队列的连接和名称,你可以在监听器类中定义 $connection 和 $queue 属性:

public $connection = 'redis';
public $queue = 'listeners';

分发事件
也就是触发这个事件,这样该事件的所有监听者都会陆续被执行。类似于投递队列。
辅助函数 event 全局可以访问,你可以在应用中的任何位置进行调用:

class OrderController extends Controller
{
    public function ship($orderId)
    {
        $order = Order::findOrFail($orderId);

        // 订单发货逻辑…

        event(new OrderShipped($order));
    }
}
发布了412 篇原创文章 · 获赞 25 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/raoxiaoya/article/details/103481006