我們所熟知的Http server-client架構一向都是由client向server發送請求,server再吐回對應的內容。
那假如今天我們希望能在server資料庫被更動時立即通知client端,即時更新使用者介面,除了client端不斷的輪詢來取得後端資料外,是否有更高端、有效率的方式?
我們可以透過WebSocket實作上述的功能,Laravel中的BroadCast支援Redis和Pusher這兩個服務,而這篇文章會示範如何透過簡單的pusher來完成主動發送事件內容到前端畫面
先在eventServiceProvider中加入
'App\Events\Event名稱' => [
'App\Listeners\Event名稱',
],
範例--新增食物:
'App\Events\FoodAdded' => [
'App\Listeners\FoodAddedListener',
],
接著用artisan產生event $php artisan event:generate
event class裡描述的是該事件的內容,事件如何才會被觸發則是在controller中被定義
範例是新增食物主動推播到訂閱指定餐廳的頻道的事件
專案目錄下的app/Event/FoodAdded.php
<?php
namespace App\Events;
//加入會用到的model
use App\Food;
use App\Restaurant;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class FoodAdded implements ShouldBroadcast //若是主動發送推播通知的事件,這邊要改成ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $restaurant;
public $food;
public $class = 'food added';
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(Food $food)
{ //這邊描述的是推播通知的內容
$this->food = $food;
$this->restaurant = Restaurant::find($food->restaurant_id);
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
//定義新增食物的通知只會發送到指定的餐廳
return ['food-channel.'.$this->restaurant->id];
}
public function broadcastAs()
{
//命名推播的事件
return 'food-event';
}
}
FoodController.php
function store(Request $request)
{
//...
$food = Food::create([
'name' => $request->name,
'remaining' => $request->remaining,
'original_price' => $request->original_price,
'discounted_price' => $request->discounted_price,
'image' => $parameters['image'],
'restaurant_id' => $request->restaurant_id,
]);
//每當該餐廳有食物上架時,會觸發FoodAdded事件,發送推播通知訂閱該餐廳的頻道
event(new FoodAdded($food));
return response()->json($food, 200);
}
此範例用Pusher來實作推播通知
首先要在專案目錄下安裝pusher套件 composer require pusher/pusher-php-server
接著更改.env內容:
BROADCAST_DRIVER=pusher
//以下欄位要先去pusher官網申請一組專案,申請完後即可在官網拿到以下四個欄位
PUSHER_APP_ID='你的pusher app id'
PUSHER_APP_KEY='你的pusher app key'
PUSHER_APP_SECRET='你的pusher app secret'
PUSHER_APP_CLUSTER='你的pusher app cluster'
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
Pusher官網有提供一個簡單的blade範例來檢查自己寫的推播通知事件是否能成功發送
<!DOCTYPE html>
<head>
<title>New Food Pusher Test</title>
<script src="https://js.pusher.com/5.1/pusher.min.js"></script>
<script>
var restaurant_id = 2; //測試2號餐廳食物上架時能不能收到主動發送的通知
// Enable pusher logging - don't include this in production
Pusher.logToConsole = true;
var pusher = new Pusher('{{env('MIX_PUSHER_APP_KEY')}}', {
cluster: '{{env('MIX_PUSHER_APP_CLUSTER')}}',
forceTLS: true
});
var channel = pusher.subscribe('food-channel.'+restaurant_id);
channel.bind('food-event', function(data) {
console.log('Pusher');
alert(JSON.stringify(data));
});
</script>
</head>
<body>
<h1>New Food Pusher Test</h1>
<p>
Try publishing an event to channel <code>food-channel+restaurant_id</code>
with event name <code>food-event</code>.
<script>
</script>
</p>
</body>
pusher.min.js:8 Pusher : : [{"type":"WebSocketError","error":{"type":"PusherError","data":{"code":4005,"message":"Path not found"}}}]
Solution:
.env要加上
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
2.前端看不到pusher該顯示的資料
Solution:檢查 .env
BROADCAST_DRIVER=pusher