我们所熟知的Http server-client架构一向都是由client向server发送请求,server再吐回对应的内容。
那假如今天我们希望能在server资料库被更动时立即通知client端,即时更新使用者介面,除了client端不断的轮询来取得后端资料外,是否有更高端、有效率的方式?
我们可以透过WebSocket实作上述的功能,Laravel中的BroadCast支援Redis和Pusher这两个服务,而这篇文章会示範如何透过简单的pusher来完成主动发送事件内容到前端画面
event
第一步:注册eventServiceProvider到Laravel Container中
先在eventServiceProvider中加入
'App\Events\Event名称' => [ 'App\Listeners\Event名称', ],
範例--新增食物:
'App\Events\FoodAdded' => [ 'App\Listeners\FoodAddedListener', ],
接着用artisan产生event $php artisan event:generate
第二步:撰写event内容
event class里描述的是该事件的内容,事件如何才会被触发则是在controller中被定义
範例是新增食物主动推播到订阅指定餐厅的频道的事件
专案目录下的app/Event/FoodAdded.php
<?phpnamespace App\Events;//加入会用到的modeluse 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来实作推播通知
首先要在专案目录下安装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}"
我写的是后端api,要怎么知道推播通知有没有成功?
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>
遇到的issue
浏览器的console: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