Pusherを使ってLaravelでリアルタイム通信をしよう

#はじめに
Pusherを使ってLaravelでリアルタイム通信を実装しました！
プログラミングを初めて４ヶ月が経ちましたが、これまでで１番の難敵だったので、備忘録を兼ねて書き記しておきたいと思います。
※この記事ではリアルタイムで投稿を表示させる仕組みについて、書いていきます！
※PusherとはWebsocletを利用した双方向通信を可能にしたAPIサービス（らしい）
※自分自身、まだ未熟のため、同じくらいのレベル感の初心者の方に伝わることを願っています。

#アプリの登録をしよう
pusherの会員登録をした後に、アプリを登録します。
今回はLaravelとJQueryで実装をします。
<img width="741" alt="スクリーンショット 2019-08-10 11.16.44.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357734/87080ee7-d9e5-100f-15da-1a58be8b6a2c.png">

登録が完了すると、Getting Startedのページへ移動します。
多くの必要情報はこちらのページに記載されているので、
何度も行ったり来たりする形になると思います。

#Laravelのフォルダ内ですること
#その1. “/config/app.php”の”providers”の以下のコメントアウトを外す。

```App\Providers\AppServiceProvider::class,
        App\Providers\AuthServiceProvider::class,
        //下記の１行をコメントアウトから外す
        App\Providers\BroadcastServiceProvider::class,
        App\Providers\EventServiceProvider::class,
        App\Providers\RouteServiceProvider::class,
```

#その２.諸々のインストール

```
composer require pusher/pusher-php-server "~3.0"
```

```
npm install --save laravel-echo pusher-js
```
composerを用いてpusherを利用するにあたって必要なファイルをインストール。
併せてLaravel EchoとpusherJsもインストールします。

resource/js内のbootstrap.js内にpusher.jsに関わる記述が加えられているので、
下記のように変更。

```
window.Pusher = require("pusher-js");

window.Echo = new Echo({
  broadcaster: "pusher",
  key: "your app key",
  cluster: "ap3",
  encrypted: true
});
```

#その３．envへの追記
登録したPusher内のGetting Start内の必要情報を.envに記入します。

```
BROADCAST_DRIVER=pusher
PUSHER_APP_ID=your app id 
PUSHER_APP_KEY=your app key
PUSHER_APP_SECRET=your app secret
PUSHER_APP_CLUSTER=ap3
```
※クオーテーションマークは不要です。
※BROADCAST_DRIVERはデフォルトだとPUSHER_APPと離れた場所にあるため、見落とし注意！

#その4.Eventを作る

```
php artisan make:event Event名
```
App/Events　内にファイルが出来ます。
このファイルを利用して、Pusherと操作が繋がっていきます。

```
<?php

namespace App\Events;

//利用したいデータベースを参照
use App\Post;
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;

class PusherEvent implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    //利用する変数追加
    public $posts;

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

    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return new Channel('channelName');
    }
}
```
利用したいデータベースを参照し、publicで変数を取得した後、constantで受け取ります。
broadcastOn()ではチャンネル名を指定しています。（※後述）

#その5.チャンネルを繋げる
route/channel.php内を下記のように書き換えます。

```
<?php
Broadcast::channel('channelName', function (){
    return true;
});
```
broadcastOn()で定義したチャンネルの名前を転記します。

#その6.コントローラー上で作業

フォームから投稿の処理をしている箇所にpusherの処理を追記。

```
 　　　　 // Eloquent モデル
        $posts = new Post;
        $posts->title = $request->title;
        $posts->description = $request->description;
        $posts->save();
        //pusherの処理
    　   event(new PusherEvent($posts));
```
ここまで作業を完了して、formから入力した値がpusher上のDebag Consoleに届いていたら、
下準備が完了です。

#その７.実際にやってみた

送信フォーム

```
 <!-- 送信フォーム -->
<form enctype="multipart/form-data" action="" method="POST" class="form-horizontal">
{{ csrf_field() }}
 <!-- postで送る時は必ず {{ csrf_field() }}をつける。セキリュティを上げる為のトークンとして利用 -->
<div class="form-group">
<div class="col-sm-6">
 <input type="text" name="title" class="form-control" placeholder="タイトル" id="title">
 <textarea name="description"  cols="30" rows="10"  class="form-control" placeholder="入力してください" id="description"></textarea>
 </div>
 </div>
 <!-- 登録ボタン -->
<div class="form-group">
 <div class="col-sm-offset-3 col-sm-6">
 <button type="submit" class="btn btn-primary" id="submit">投稿</button>
</form> 
 <!-- 表示される部分 -->
 <td class="table-text" id="board">
 <div><label for="title">タイトル：</label>{{ $write->title }}</div> 
<div><label for="description">内容：</label>{{ $write->description }}</td>
```

私は下記の記事を参考にさせて頂き実装しました。（本当にありがとうございます！）
https://nextat.co.jp/staff/archives/213
JSの部分

```
$(document).ready(function() {
  $.ajaxSetup({
    headers: {
      "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr("content")
    }
  });
  $("#submit").click(function() {
    const url = "/posts";
    $.ajax({
      url: url,
      data: {
        title: $("#title").val(),
        description: $("#description").val(),
      },
      method: "POST"
    });
    return false;
  });
  window.Echo.channel("channelName").listen("PusherEvent", e => {
    $("#board").prepend(
      "<div><label>タイトル：</label>" + e.posts.title + "</div>"
    );
    $("#board").prepend(
      "<div><label>内容：</label>" + e.posts.description + "</div>"
    );
  });
});
```
 window.Echo.channelにその４のEventで作成したチャンネルの名前、表示の際に同じファイルで定義した変数を入力します。

私はpublicにjsファイルを作り、送信フォームに読み込ませました。
ローカルでブラウザを２つ開き、Formタグで作った入力欄から値を送信。
リアルタイムで両方のブラウザに反映されていれば成功です。
ここまで３日かかったので、完成した時は思わずガッツポーズでした！！！！！！


#個人的に詰まったところ
・envとbootstrap.jsへの記述不足（特に.envのBROADCAST_DRIVERが見つけにくい）
・JSファイルの読み込みエラー
https://qiita.com/u-dai/items/83766b69a0e18488b005
こちらの記事で解説いただいてることを知らず、ずっと悩み続けた😅
app.jsとその２で追記したbootstrap.jsが送信フォームに読み込まれていないと、エラーが出ます。またJSのバージョンをコンパイルする必要もあるようで、上記のやり方が今回の解でした。

#参考にさせて頂いた記事やソースコード
実装にあたり一番大切な幹の部分を余すことなく解説されています。
https://nextat.co.jp/staff/archives/213
英語のドキュメントでは解説記事やソースコードが多かった。
https://github.com/tlaverdure/laravel-echo-server/issues/158
https://github.com/jacurtis/Packt-Laravel-Echo
JSの読み込みでエラーが出たらこちらの投稿を確認
https://stackoverflow.com/questions/52994606/laravel-echo-uncaught-typeerror-cannot-read-property-channel-of-undefined