laravel5.7で、リアルタイムチャットや通知に必要なpusher.comを使ってみた
リアルタイムチャットや通知には、サーバ側からブラウザ側へアクションをしないとダメなのだが、そもそもHTTPはブラウザ側からの一期一会のアクションしか存在しない(郵便ポストに投函して返事を待つイメージ)
GET(このファイルくれ!)→index.html
POST(このデータでなんとかして!)→index.html(申し込みフォームや検索など)
なので、node.jsなどを使ってWebSocket(電話のようにつなげっぱなしな状態)を利用しないと、phpではリアルタイムチャットや通知を実現できない!
一番最初の下準備として、PusherのPHP SDK, Laravel Echo(ブロードキャスト/WebSocket)をインストールする
| 1 2 | composer require pusher/pusher-php-server npm install --save laravel-echo pusher-js | 
pusher.comのアカウントとCreate new app(laravel+JS)を作ったら、Getting Startedに、受信処理(HTML)と送信処理(PHP)があるので、そのままコピペすれば、laravelでpusher送受信が出来る
routes/web.php(空文字の部分は、各自のIDやKEYが入力されているはず!)
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | <?php // pusherからの受信処理 Route::get('/receive', function() {     // Getting Startedの左側のjavascript受信処理(HTML)をコピペ     return <<<EOF <!DOCTYPE html> <head>   <title>Pusher Test</title>   <script src="https://js.pusher.com/5.0/pusher.min.js"></script>   <script>     // Enable pusher logging - don't include this in production     Pusher.logToConsole = true;     var pusher = new Pusher('', {       cluster: '',       forceTLS: true     });     var channel = pusher.subscribe('my-channel');     channel.bind('my-event', function(data) {       alert(JSON.stringify(data));     });   </script> </head> <body>   <h1>Pusher Test</h1>   <p>     Try publishing an event to channel <code>my-channel     with event name <code>my-event</code>.   </p> </body> EOF; }); // pusherへの送信処理 Route::get('/send', function(){   // pusherの左側のPHPタブの送信処理をコピペ   $options = array(     'cluster' => '',     'useTLS' => true   );   $pusher = new Pusher\Pusher(     '',     '',     '',     $options   );   $data['message'] = 'hello world';   $pusher->trigger('my-channel', 'my-event', $data); }); | 
/receiveのページを開いた状態で、/sendのページにアクセスすると、pusher.com経由で、/receiveページにalertが表示される!!
これはこれで簡単だけど、あちこちに同じ処理を書くのは大変なので、laravelのお作法に沿って記述する。
0, config/app.phpで、なぜかブロードキャストだけコメントアウトされているので、コメントインする
| 1 | Illuminate\Broadcasting\BroadcastServiceProvider::class, | 
1, .envに、pusher.comへのアクセス情報を記述。これでlaravelからpusherへアクセス出来るようになる。
| 1 2 3 4 5 6 7 8 | #logからpusherに変更 BROADCAST_DRIVER=pusher  # pusher.comのApp Keysに同じ順番で書いてあるのでコピペ PUSHER_APP_ID= PUSHER_APP_KEY= PUSHER_APP_SECRET= PUSHER_APP_CLUSTER= | 
2, チャット用のMessageモデルを作る
| 1 2 3 4 5 6 7 | # チャット用メッセージモデル・コントローラー・マイグレーション・ダミーデータを生成。 php artisan make:model Message --all # app/Message.phpに、レコード書き込みのため protected $guarded = ['id']; しておく # messageテーブルに、$table->text('text');だけ追加 # チャットのメッセージ作成イベントを生成 php artisan make:event MessageCreated | 
3, Eloquentには、最初からCRUD操作する時にpusherに通知する機能がある!
Laravel 5.1 Broadcastig EventsでPusherを利用してリアルタイム更新アプリを作成するには
4, app/Providers/AppServiceProvider.phpに、チャットで発言されたら、イベント発火するように記述
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | use Event; use App\Message; use App\Events\MessageCreated; class AppServiceProvider extends ServiceProvider {     public function boot()     {         // チャットで発言されたら、発火         Message::created(function ($message) {             // laravel5.8でfire廃止、event()に置き換える。             //Event::fire(new MessageCreated($message));             event(new MessageCreated($message));         });     } | 
5, Messageレコードを新規作成されたら、pusher通知を行うようにする。
※pusher通知の書き込み(event)には、channel名, event名, app_id, key, secret, clusterが必須!
pusher通知の読み込み(listener)には、channel名, event名, app_id, clusterが必須!(key, secretは不要)
同じapp_id内で、channel名やevent名で通知を使い分ける。
app/Events/MessageCreated.php
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | <?php namespace App\Events; use App\Message; // チャットのメッセージ 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; //  implements ShouldBroadcastを追加しないとpusherへ送信されない。 class MessageCreated implements ShouldBroadcast {     use Dispatchable, InteractsWithSockets, SerializesModels;     // このイベント(messageレコード新規作成)が発火したら、メッセージ文字列を受け取り、pusherへ投げる(このレコードの全カラム情報)     // レコード更新・削除にもイベント追加できる。AppServiceProviderのboot()にも記述する     public $message;     public function __construct(Message $message)     {         $this->message = $message;     }     // チャンネル名を設定     public function broadcastOn()     {         return new Channel('my-channel');     }     // イベント名を設定     public function broadcastAs()     {         return 'my-event';     } } | 
6, resources/views/index.blade.phpなどに、受信処理(HTML)をコピペしておく
最初の/receiveを利用しても良い。
7, 適当なルーティングで、Messageレコード新規作成して、受信処理(HTML)が正常に動作するか確認する。
| 1 2 3 4 5 6 7 8 9 | Route::get('/write', function () {     App\Message::create(['text'=>'hello']);     return 'ok'; }); Route::get('/', function () {     return view('index'); }); | 
これで、なんとなくpusher(リアルタイム通知)が使えるようなった(画面周りは何も作っていないのでサンプルのアラート表示のまま)ので、次はプライベート・イベント(ログインした特定のユーザ向け)を実装してみよう。
今回のは、パプリック・イベントなので、ログインとか無関係だった。