Lumen 事件

2021-09-15 14:40 更新

1、簡介

Lumen事件提供了簡單的觀察者模式實(shí)現(xiàn),允許你訂閱和監(jiān)聽?wèi)?yīng)用中的事件。事件類通常存放在app/Events目錄,監(jiān)聽器存放在app/Listeners

2、注冊事件/監(jiān)聽器

Lumen自帶的EventServiceProvider為事件注冊提供了方便之所。其中的listen屬性包含了事件(鍵)和對應(yīng)監(jiān)聽器(值)數(shù)組。如果應(yīng)用需要,你可以添加多個(gè)事件到該數(shù)組。例如,讓我們添加PodcastWasPurchased事件:

/**
 * 事件監(jiān)聽器映射
 *
 * @var array
 */
protected $listen = [
    'App\Events\PodcastWasPurchased' => [
        'App\Listeners\EmailPurchaseConfirmation',
    ],
];

3、定義事件

事件類是一個(gè)處理與事件相關(guān)的簡單數(shù)據(jù)容器,例如,假設(shè)我們生成的PodcastWasPurchased事件接收一個(gè)Eloquent ORM對象:

<?php

namespace App\Events;

use App\Podcast;
use App\Events\Event;
use Illuminate\Queue\SerializesModels;

class PodcastWasPurchased extends Event{
    use SerializesModels;

    public $podcast;

    /**
     * 創(chuàng)建新的事件實(shí)例
     *
     * @param  Podcast  $podcast
     * @return void
     */
    public function __construct(Podcast $podcast)
    {
        $this->podcast = $podcast;
    }
}

正如你所看到的,該事件類不包含任何特定邏輯,只是一個(gè)存放被購買的Podcast對象的容器,如果事件對象被序列化的話,事件使用的 SerializesModels trait將會使用PHP的serialize函數(shù)序列化所有Eloquent模型。

4、定義監(jiān)聽器

接下來,讓我們看看我們的示例事件的監(jiān)聽器,事件監(jiān)聽器在handle方法中接收事件實(shí)例。在handle方法內(nèi),你可以執(zhí)行任何需要的邏輯以響應(yīng)事件。

<?php

namespace App\Listeners;

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

class EmailPurchaseConfirmation{
    /**
     * 創(chuàng)建事件監(jiān)聽器
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * 處理事件
     *
     * @param  PodcastWasPurchased  $event
     * @return void
     */
    public function handle(PodcastWasPurchased $event)
    {
        // Access the podcast using $event->podcast...
    }
}

你的事件監(jiān)聽器還可以在構(gòu)造器中類型提示任何需要的依賴,所有事件監(jiān)聽器通過服務(wù)容器解析,所以依賴會自動注入。

停止事件繼續(xù)往下傳播

有時(shí)候,你希望停止事件被傳播到其它監(jiān)聽器,你可以通過從監(jiān)聽器的handle方法中返回false來實(shí)現(xiàn)。

4.1 事件監(jiān)聽器隊(duì)列

需要將事件監(jiān)聽器放到隊(duì)列中?沒有比這更簡單的了,只需要讓監(jiān)聽器類實(shí)現(xiàn)ShouldQueue接口即可,通過Artisan命令event:generate生成的監(jiān)聽器類已經(jīng)將接口導(dǎo)入當(dāng)前命名空間,所有你可以立即拿來使用:

<?php

namespace App\Listeners;

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

class EmailPurchaseConfirmation implements ShouldQueue{
    //
}

就是這么簡單,當(dāng)監(jiān)聽器被事件調(diào)用,將會使用Lumen的隊(duì)列系統(tǒng)通過隊(duì)列分發(fā)器自動隊(duì)列化。如果通過隊(duì)列執(zhí)行監(jiān)聽器的時(shí)候沒有拋出任何異常,隊(duì)列任務(wù)在執(zhí)行完成后被自動刪除。

手動訪問隊(duì)列

如果你需要手動訪問底層隊(duì)列任務(wù)的deleterelease方法,在生成的監(jiān)聽器中默認(rèn)導(dǎo)入的Illuminate\Queue\InteractsWithQueue trait提供了訪問這兩個(gè)方法的權(quán)限:

<?php

namespace App\Listeners;

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

class EmailPurchaseConfirmation implements ShouldQueue{
    use InteractsWithQueue;

    public function handle(PodcastWasPurchased $event)
    {
        if (true) {
            $this->release(30);
        }
    }
}

5、觸發(fā)事件

要觸發(fā)一個(gè)事件,可以使用Event門面,傳遞一個(gè)事件實(shí)例到fire方法,fire方法會分發(fā)事件到所有監(jiān)聽器:

<?php

namespace App\Http\Controllers;

use Event;
use App\Podcast;
use App\Events\PodcastWasPurchased;
use App\Http\Controllers\Controller;

class UserController extends Controller{
    /**
     * 顯示指定用戶屬性
     *
     * @param  int  $userId
     * @param  int  $podcastId
     * @return Response
     */
    public function purchasePodcast($userId, $podcastId)
    {
        $podcast = Podcast::findOrFail($podcastId);

        // Purchase podcast logic...

        Event::fire(new PodcastWasPurchased($podcast));
    }
}

此外,你還可以使用全局的幫助函數(shù)event來觸發(fā)事件:

event(new PodcastWasPurchased($podcast));

6、廣播

在很多現(xiàn)代web應(yīng)用中,web套接字被用于實(shí)現(xiàn)實(shí)時(shí)更新的用戶接口。當(dāng)一些數(shù)據(jù)在服務(wù)器上被更新,通常一條消息通過websocket連接被發(fā)送給客戶端處理。

為幫助你構(gòu)建這樣的應(yīng)用,Lumen讓通過websocket連接廣播事件變得簡單。廣播Lumen事件允許你在服務(wù)端和客戶端JavaScript框架之間共享同一事件名。

6.1 配置

Lumen支持多種廣播驅(qū)動:PusherRedis以及一個(gè)服務(wù)于本地開發(fā)和調(diào)試的日志驅(qū)動。每一個(gè)驅(qū)動都有一個(gè)配置示例。BROADCAST_DRIVER配置選項(xiàng)可用于設(shè)置默認(rèn)驅(qū)動。

廣播預(yù)備知識

事件廣播需要以下兩個(gè)依賴:

  • Pusherpusher/pusher-php-server ~2.0
  • Redispredis/predis ~1.0

隊(duì)列預(yù)備知識

在開始介紹廣播事件之前,還需要配置并運(yùn)行一個(gè)隊(duì)列監(jiān)聽器。所有事件廣播都通過隊(duì)列任務(wù)來完成以便應(yīng)用的響應(yīng)時(shí)間不受影響。

6.2 將事件標(biāo)記為廣播

要告訴Lumen給定事件應(yīng)該被廣播,需要在事件類上實(shí)現(xiàn)Illuminate\Contracts\Broadcasting\ShouldBroadcast接口。ShouldBroadcast接口要求你實(shí)現(xiàn)一個(gè)方法:broadcastOn。該方法應(yīng)該返回事件廣播”頻道“名稱數(shù)組:

<?php

namespace App\Events;

use App\User;
use App\Events\Event;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class ServerCreated extends Event implements ShouldBroadcast{
    use SerializesModels;

    public $user;

    /**
     * 創(chuàng)建新的事件實(shí)例
     *
     * @return void
     */
    public function __construct(User $user)
    {
        $this->user = $user;
    }

    /**
     * 獲取事件廣播頻道
     *
     * @return array
     */
    public function broadcastOn()
    {
        return ['user.'.$this->user->id];
    }
}

然后,你只需要和正常一樣觸發(fā)該事件,事件被觸發(fā)后,一個(gè)隊(duì)列任務(wù)將通過指定廣播驅(qū)動自動廣播該事件。

6.3 廣播數(shù)據(jù)

如果某個(gè)事件被廣播,其所有的public屬性都會按照事件負(fù)載自動序列化和廣播,從而允許你從JavaScript中訪問所有public數(shù)據(jù),因此,舉個(gè)例子,如果你的事件有一個(gè)單獨(dú)的包含Eloquent模型的$user屬性,廣播負(fù)載定義如下:

{
    "user": {
        "id": 1,
        "name": "Jonathan Banks"
        ...
    }
}

然而,如果你希望對廣播負(fù)載有更加細(xì)粒度的控制,可以添加broadcastWith方法到事件,該方法應(yīng)該返回你想要通過事件廣播的數(shù)組數(shù)據(jù):

/**
 * 獲取廣播數(shù)據(jù)
 *
 * @return array
 */
public function broadcastWith(){
    return ['user' => $this->user->id];
}

6.4 消費(fèi)事件廣播

Pusher

你可以通過Pusher的JavaScript SDK方便地使用Pusher驅(qū)動消費(fèi)事件廣播。例如,讓我們從之前的例子中消費(fèi)App\Events\ServerCreated事件:

this.pusher = new Pusher('pusher-key');

this.pusherChannel = this.pusher.subscribe('user.' + USER_ID);

this.pusherChannel.bind('App\\Events\\ServerCreated', function(message) {
    console.log(message.user);
});

Redis

如果你在使用Redis廣播,你將需要編寫自己的Redis pub/sub消費(fèi)者來接收消息并使用自己選擇的websocket技術(shù)將其進(jìn)行廣播。例如,你可以選擇使用使用Node編寫的流行的Socket.io庫。

使用Node庫socket.ioioredis,你可以快速編寫事件廣播發(fā)布所有廣播事件:

var app = require('http').createServer(handler);
var io = require('socket.io')(app);

var Redis = require('ioredis');
var redis = new Redis();

app.listen(6001, function() {
    console.log('Server is running!');});

function handler(req, res) {
    res.writeHead(200);
    res.end('');}

io.on('connection', function(socket) {
    //
});

redis.psubscribe('*', function(err, count) {
    //
});

redis.on('pmessage', function(subscribed, channel, message) {
    message = JSON.parse(message);

});

7、事件訂閱者

事件訂閱者是指那些在類本身中訂閱到多個(gè)事件的類,從而允許你在單個(gè)類中定義一些事件處理器。訂閱者應(yīng)該定義一個(gè)subscribe方法,該方法中傳入一個(gè)事件分發(fā)器實(shí)例:

<?php

namespace App\Listeners;

class UserEventListener{
    /**
     * 處理用戶登錄事件
     */
    public function onUserLogin($event) {}

    /**
     * 處理用戶退出事件
     */
    public function onUserLogout($event) {}

    /**
     * 為訂閱者注冊監(jiān)聽器
     *
     * @param  Illuminate\Events\Dispatcher  $events
     * @return array
     */
    public function subscribe($events)
    {
        $events->listen(
            'App\Events\UserLoggedIn',
            'App\Listeners\UserEventListener@onUserLogin'
        );

        $events->listen(
            'App\Events\UserLoggedOut',
            'App\Listeners\UserEventListener@onUserLogout'
        );
    }

}

7.1 注冊一個(gè)事件訂閱者

訂閱者被定義后,可以通過事件分發(fā)器進(jìn)行注冊,你可以使用EventServiceProvider上的$subcribe屬性來注冊訂閱者。例如,讓我們添加UserEventListener

<?php

namespace App\Providers;

use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

class EventServiceProvider extends ServiceProvider{
    /**
     * 事件監(jiān)聽器映射數(shù)組
     *
     * @var array
     */
    protected $listen = [
        //
    ];

    /**
     * 要注冊的訂閱者
     *
     * @var array
     */
    protected $subscribe = [
        'App\Listeners\UserEventListener',
    ];
}
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號