laravel 基礎(chǔ)教程 —— 事件

事件

簡介

laravel 的事件提供了一種簡單的觀察者實現(xiàn)收叶。它允許你在應用中進行訂閱和監(jiān)聽事件童太。事件類通常都是存儲在 app/Events 目錄中拆檬,而他們的監(jiān)聽者都是存儲在 app/Listeners 目錄中惜傲。

注冊事件/監(jiān)聽者

EventServiceProvider 提供了一個注冊所有事件監(jiān)聽者的方便的場所约素。它的 listen 屬性包含了一個所有事件(keys)以及他們的監(jiān)聽者(values)所組成的數(shù)組届良。當然,你可以在這個數(shù)組中添加任何你需要的事件圣猎。比如士葫,讓我們添加 PodcastWasPurchased 事件:

/**
 * The event listener mappiings for the application.
 *
 * @var array
 */
 protected $listen = [
   'App\Events\PodcastWasPurchased' => [
     'App\Listeners\EmailPurchaseConfirmation',
   ],
 ],

生成事件/監(jiān)聽者類

當然,每次都手動的去創(chuàng)建一個事件文件和一個監(jiān)聽者文件是很麻煩的事情送悔,所以慢显,你可以在 EventServiceProvider 中添加事件和監(jiān)聽者然后使用 event:generate 命令來自動生成。這個命令會生成 EventServiceProvider 中的所有列出的事件和監(jiān)聽者放祟,當然鳍怨,已經(jīng)存在的事件和監(jiān)聽者不會重新生成:

php artisan event:generate

手動的注冊事件

通常,事件應該被注冊在 EventServiceProvider$listen 數(shù)組中跪妥,事實上鞋喇,你可以使用 Event 假面的事件分發(fā)器或者一個 Illuminate\Contracts\Events\Dispatcher 的實現(xiàn)來手動注冊事件:

/**
 * Register any other events for your application
 *
 * @param \Illuminate\Contracts\Events\Dispatcher $events
 * @return void
 */
 public function boot(DispatcherContract $events)
 {
   parent::boot($events);

   $events->listen('event.name', function ($foo, $bar) {
     //
   });
 }

事件監(jiān)聽通配符

你可以在注冊監(jiān)聽器的時候使用 * 來作為通配符。這允許你來在同一個監(jiān)聽器中監(jiān)聽多種事件眉撵。通配符監(jiān)聽器接收整個事件數(shù)據(jù)數(shù)組作為第一個參數(shù):

$events->listen('event.*', function (array $data) {
  // 
});

定義事件

一個事件類只是簡單的數(shù)據(jù)存儲器侦香,它應該持有事件相關(guān)的信息.比如,讓我們假設(shè)我們生成的 PodcastWasPurchased 事件應該接收一個 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;

  /**
   * Create a new event instance.
   *
   * @param Podcast $podcast
   * @return void
   */
   public function __construct(Podcast $podcast)
   {
     $this->podcast = $podcast;
   }
}

就如你所看到的纽疟,這個事件類并沒有包含什么業(yè)務邏輯罐韩。它只是簡單的包含了一個被購買的 Podcast 對象。SerializesModels trait 被用來序列化 Eloquent 模型污朽,如果事件對象是使用 PHP 的 serialize 方法被進行序列化散吵,那么 SerializesModels 就會優(yōu)雅的將其內(nèi)部的 Eloquent 對象序列化。

定義監(jiān)聽者

接著蟆肆,讓我們來看一下針對我們上面舉例的事件的監(jiān)聽者矾睦。事件監(jiān)聽者會在其 handle 方法中接收事件的實例。event:generate 命令會自動的在 handle 方法中引入其對應的事件的類型提示炎功。你可以在 handle 方法中來提供對事件的響應邏輯:

<?php

namespace App\Listeners;

use App\Events\PodcastWasPurchased;

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

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

你的事件監(jiān)聽者也可以在構(gòu)造函數(shù)中進行類型提示來注入依賴枚冗。所有的事件監(jiān)聽器都是通過 laravel 的服務容器解析的。所以蛇损,它們的依賴可以被自動的注入:

use Illuminate\Contracts\Mail\Mailer;

public function __construct(Mailer $mailer)
{
  $this->mailer = $mailer;
}

停止傳遞事件

有時候赁温,你可能希望停止傳遞事件到后續(xù)的監(jiān)聽者中坛怪。你可以在監(jiān)聽者的 handle 方法中返回 false,這樣后續(xù)的監(jiān)聽者將不再進行事件的響應股囊。

監(jiān)聽者隊列化

需要對事件監(jiān)聽者進行隊列化袜匿?沒有比這更簡單的了。直接添加 ShouldeQueue 接口到監(jiān)聽者類中就可以了毁涉。通過 event:generate Artisan 命令生成的監(jiān)聽者已經(jīng)在當前的命名空間中添加了對這個接口的支持沉帮,所以你立即就可以使用:

<?php

namespace App\Listeners;

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

class EmailPurchaseConfirmation implements ShouldQueue
{
  //
}

就這么簡單!當事件觸發(fā)時贫堰,事件分發(fā)器會使用 laravel 的隊列系統(tǒng)對監(jiān)聽器進行自動的隊列化調(diào)用穆壕。如果監(jiān)聽器隊列化執(zhí)行的過程中沒有異常出現(xiàn),隊列任務會自動的在監(jiān)聽器進程完成后進行刪除其屏。

手動的訪問隊列

如果你需要手動的訪問底層隊列任務的 deleterelease 方法喇勋。那么你就可以直接去使用它們。Illuminate\Queue\InteractsWithQueue trait 對默認生成的監(jiān)聽器提供了訪問這些方法的權(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);
    }
  }
}

觸發(fā)事件

你可以使用 Event 假面來進行事件的觸發(fā)偎行,你需要傳遞一個事件實例到 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
{
  /**
   * Show the profile for the given user.
   *
   * @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));
  } 
}

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

event( new PodcastWasPurchased($podcast));

廣播事件

很多現(xiàn)代化的應用中蛤袒,會使用 web sockets 來實現(xiàn)實時交互的用戶接口熄云。當一些數(shù)據(jù)在服務端變更時,一條消息會通過 websocket 連接來傳遞到客戶端進行處理妙真。

為了幫助你構(gòu)建這種類型的應用缴允。laravel 使通過 websocket 連接進行廣播事件變的非常簡單。laravel 允許你廣播事件來共享事件的名稱到你的服務端和客戶端的 JavaScript 框架珍德。

配置

所有的事件廣播配置選項都被存儲在 config/broadcasting.php 配置文件中练般。laravel 支持多種開箱即用的廣播驅(qū)動:PusherRedislog 驅(qū)動來提供本地開發(fā)和調(diào)試锈候。在這個配置文件中包含了每種驅(qū)動的配置示例薄料。

廣播先決條件

事件廣播需要以下依賴:

  • Pusher: pusher/pusher-php-server ~2.0
  • Redis: predis/predis ~1.0

隊列化先決條件

在進行事件廣播之前,你需要先配置好隊列監(jiān)聽器泵琳。所有的廣播都是通過隊列任務來異步執(zhí)行的摄职,這樣應用響應就不會受到影響。

對事件進行廣播

你需要實現(xiàn) Illuminate\Contracts\Broadcasting\ShouldBroadcast 接口在事件類中获列,以通知 laravel 所給定的事件需要被廣播琳钉。ShouldBroadcast 接口只要求你實現(xiàn)一個單一的方法:broadcastOn.該方法應該返回一個事件應該被廣播的通道名稱:

<?php

namespace App\Events;

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

class ServerCreated extends Event implements ShouldBroadcast
{
  use SerializesModels;

  public $user;

  /**
   * Create a new event instance.
   *
   * @return void
   */
  public function __construct(User $user)
  {
    $this->user = $user;
  }

  /**
   * Get the channels the event should be broadcast on.
   *
   * @return array
   */
   public function broadcastOn()
   {
     return ['user.' . $this->user->id];
   }
}

然后,你只需要像平常那樣去觸發(fā)事件就可以了蛛倦。一旦事件被觸發(fā),隊列任務會自動的廣播事件到你指定的廣播驅(qū)動中啦桌。

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

當一個事件被廣播時溯壶,事件類的所有 public 屬性都會被序列化并作為廣播的載荷及皂,允許你的前端 JavaScript 應用來訪問所有的開放屬性。比如且改,如果事件類中只有一個開放的 $user 屬性验烧,該屬性包含了一個 Eloquent 模型,那么廣播的載荷將會像這樣:

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

事實上又跛,你可以通過在事件中添加 broadcastWith 方法來對廣播載荷擁有更多的控制碍拆。該方法應該返回一個數(shù)組:

/**
 * Get the data to broadcast.
 *
 * @return array
 */
 public function broadcastWith()
 {
   return ['user' => $this->user->id];
 }

定制化事件廣播

定制事件名稱

默認的,廣播事件的名稱會使用事件類的全名慨蓝。比如感混,如果事件的類名是 App\Events\ServerCreated,那么廣播事件的名稱就是 App\Events\ServerCreated礼烈。你可以通過定義 broadcastAs 方法來自定義廣播事件的名稱:

/**
 * Get the broadcast event name
 *
 * @return string
 */
 public function broadcastAs()
 {
   return 'app.server-created';
 }

定制化隊列

默認的弧满,所有的事件廣播都是通過 queue.php 配置文件中所指定的默認隊列配置來進行的。你可以通過在事件廣播類中添加一個 onQueue 方法來指定所使用的隊列此熬。該方法應該返回一個你所期望使用的隊列的名稱:

/**
 * Set the name of the queue the event should be placed on.
 *
 * @return string
 */
 public function onQueue()
 {
   return 'your-queue-name';
 }

對接廣播事件

Pusher

你可以使用 Pusher 驅(qū)動的 JavaScript SDK來輕松的對接廣播事件庭呜。比如,讓我們消費前面例子中的 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 發(fā)布/訂閱消費者來接收消息募谎,你可以自由的選擇使用 websocket 技術(shù)來進行事件的廣播。比如阴汇,你可以選擇使用基于 Node 受歡迎的 Socket.io 類庫数冬。

你可以使用 socket.ioioredis Node 類庫來快速的編寫事件廣播員來發(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 (subscrbed, channel, message) {
  message = JSON.parse(message);
  io.emit(channel + ':' + message.event, message.data);
});

事件訂閱

事件訂閱允許一個類在其內(nèi)部訂閱多個事件。這允許你在同一個文件中對多個事件進行處理鲫寄。訂閱者應該定義 subscribe 方法吉执。該方法會被傳遞一個事件分發(fā)器實例:

<?php

namespace App\Listeners;

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

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

    /**
     * Register the listeners for the subscriber.
     *
     * @param Illuminate\Events\Dispatcher $events
     */
     public function subscribe($events)
     {
       $events->listen(
         'App\Events\UserLoggedIn',
         'App\Listeners\UserEventListener@onUserLogin'
        );
       $events->listen(
         'App\Events\UserLoggedOut',
         'App\Listeners\UserEventListener@onUserLogout'
        );
     }
}

注冊一個事件訂閱者

當你的訂閱者被定義完成之后,它應該在事件分發(fā)器中進行注冊地来。你可以使用 EventServieProvider$subscribe 屬性來注冊訂閱者戳玫。比如,讓我們來添加一個 UserEventListener:

<?php

namespace App\Providers;

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

class EventServiceProvider extends ServiceProvider
{
  /**
   * The event listener mapping for the application.
   *
   * @var array
   */
   protected $listen = [
     //
   ];

   /**
    * The subscriber classes to register.
    *
    * @var array
    */
    protected $subscribe = [
      'App\Listeners\UserEventListener',
    ];
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末未斑,一起剝皮案震驚了整個濱河市咕宿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蜡秽,老刑警劉巖府阀,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異芽突,居然都是意外死亡试浙,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門寞蚌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來田巴,“玉大人钠糊,你說我怎么就攤上這事∫疾福” “怎么了抄伍?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長管宵。 經(jīng)常有香客問我截珍,道長,這世上最難降的妖魔是什么箩朴? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任岗喉,我火速辦了婚禮,結(jié)果婚禮上隧饼,老公的妹妹穿的比我還像新娘沈堡。我一直安慰自己,他們只是感情好燕雁,可當我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布诞丽。 她就那樣靜靜地躺著,像睡著了一般拐格。 火紅的嫁衣襯著肌膚如雪僧免。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天捏浊,我揣著相機與錄音懂衩,去河邊找鬼。 笑死金踪,一個胖子當著我的面吹牛浊洞,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播胡岔,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼法希,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了靶瘸?” 一聲冷哼從身側(cè)響起苫亦,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎怨咪,沒想到半個月后屋剑,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡诗眨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年唉匾,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片匠楚。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡肄鸽,死狀恐怖卫病,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情典徘,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布益咬,位于F島的核電站逮诲,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏幽告。R本人自食惡果不足惜梅鹦,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望冗锁。 院中可真熱鬧齐唆,春花似錦、人聲如沸冻河。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽叨叙。三九已至锭弊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間擂错,已是汗流浹背味滞。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留钮呀,地道東北人剑鞍。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像爽醋,于是被迫代替她去往敵國和親蚁署。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理子房,服務發(fā)現(xiàn)形用,斷路器,智...
    卡卡羅2017閱讀 134,656評論 18 139
  • 原文鏈接 必備品 文檔:Documentation API:API Reference 視頻:Laracasts ...
    layjoy閱讀 8,607評論 0 121
  • 隊列 簡介 laravel 的隊列服務對各種不同的后臺隊列服務提供了統(tǒng)一的 API证杭。隊列允許你延遲執(zhí)行消耗時間的任...
    Dearmadman閱讀 20,744評論 7 26
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,116評論 25 707
  • 兒子最近要上幼兒園了田度,先去參加了親子班提前適應幼兒園的生活。結(jié)果他媽媽陪著去還能玩一會解愤,媽媽只要不見了镇饺,就會哭哭鬧...
    秋天聽風閱讀 355評論 0 0