ウェブソケットと同様にリアルタイム(まがい)で画面の一部を更新する方法に、ポーリングがあります。ウェブソケットではサーバーから更新情報をブラウザ(クライアント)へプッシュするのに対して、ポーリングは逆にブラウザからサーバーに情報を一定の時間で取得(ポーリング)しに行きます。それゆえに、PusherReverbのようなウェブソケットサーバーは必要でなく簡単なセットアップとなります。このポーリングをとても人気があるLivewireを使用してプログラムしてみます。

欲しいもの

実用的なものでないですが、とても簡単なデモとします。

上の画面のように、ログイン画面が表示されたらログインできる残りの秒数を10秒からカウントダウンします。10秒過ぎたからと言ってログインが無効になるわけではありません。単に残りの秒数を全ページ更新せずに表示するだけです。

通常なら単にjavascriptでsetTimeout()あるいはsetInterval()を駆使して実現できることを、javascriptを一切使用せずにサーバーから残り秒数を取得して表示します。

Livewireのインストール

まず、Livewireのパッケージのインストール。

$ composer require livewire/livewire

今回はこれだけ。設定は何も要りません。

コンポーネントの作成

次にタイマーのコンポーネントを作成します。artisanでモデルを作成するようにコンポーネントを作成してくれます。

$ php artisan make:livewire Timer

これで作成されるファイルは2つあります。1つはコンポーネントでもう1つはそれに使用されるブレードです。

        new file:   app/Livewire/Timer.php
        new file:   resources/views/livewire/timer.blade.php

コンポーネントのコードを以下のように編集します。


namespace App\Livewire;

use Livewire\Component;

class Timer extends Component
{
    public $remaining = 11;

    public function render()
    {
        $this->remaining--;

        return view('livewire.timer', [
            'remaining' => $this->remaining,
        ]);
    }
}

$remainingの変数が残り秒数の値となりますが、最初に11秒と設定しておき、そこから1を引いた10秒から表示です。
正確に行うには開始時点での日時をキープしてrender()がコールされるたびにその時点での日時から経過秒数を計算ですが、ここは1秒おきに関数がコールされるとしてシンプルにします。ちなみに、Livewireではサーバーでの複数のリクエストを通して同じオブジェクトにアクセスが可能で、$remainingの値は毎回初期化されずステートをキープします。

次はコンポーネントのブレードの編集です。先のコンポーネントのクラス名と関連させてtimer.blade.phpとなっています。

   <div wire:poll.1s>
        残りの秒数:{{ $remaining }}
    </div>

上のコードで大事なのは、wire:poll.1sの部分です。これがポーリングを指示しています。.1sは、1秒ごとにポーリングです。これがないとデフォルトは、2.5秒でポーリングとなります。

さて、このコンポーネントをログインのブレードに入れます。見てのとおり<livewire:timer />だけとシンプルです。

<x-guest-layout>
    <!-- Session Status -->
    <x-auth-session-status class="mb-4" :status="session('status')" />

    <form method="POST" action="{{ route('login') }}">
        @csrf

        <livewire:timer /> <!-- ここでコンポーネントを使用 -->

        <!-- Email Address -->
...

この時点で不思議に思うのは、Livewireのおかげでjavascriptのコードは一切も書かないのですが、どこかでLivewireのライブラリ(javascript)をコールされているはずですよね。今のところブレードには前もって含まれてはいません。しかし、レンダーされた画面のHTMLソースを見ると以下のように、自動でスクリプトがインジェクトされています。

....
    <!-- Livewire Scripts -->
<script src="/livewire/livewire.js?id=cc800bf4"   data-csrf="ys60l3TQzop1efcgNKZjtUlZ4mX9WxZzlpiVjoUN" data-update-uri="/livewire/update" data-navigate-once="true"></script>
</body>
</html>

実際にログイン画面を表示してブラウザーのインスペクターでポーリングが行われているが見てみましょう。

たくさんPOSTのリクエストが実行されています。リクエストの詳細では、
http://127.0.0.1:8000/livewire/update
にアクセスしているのが分かります。

このルートも手動で設定してはいないのに自動設定されています。チェックしてみましょう。

$ php artisan route:list | grep livewire
  GET|HEAD  livewire/livewire.js ......................... Livewire\Mechanisms › FrontendAssets@returnJavaScriptAsFile
  GET|HEAD  livewire/livewire.min.js.map ................................... Livewire\Mechanisms › FrontendAssets@maps
  GET|HEAD  livewire/preview-file/{filename} livewire.preview-file › Livewire\Features › FilePreviewController@handle
  POST      livewire/update ...................... livewire.update › Livewire\Mechanisms › HandleRequests@handleUpdate
  POST      livewire/upload-file .............. livewire.upload-file › Livewire\Features › FileUploadController@handle

livewire関連がいろいろとありますが、先ほどのルートも含まれていました。

タイマーを止める

さて、最後にもう1つやることあります。先ほどのプログラムでは、ほったらかしにすると残りの秒数がマイナスになってしまいます。

残り秒数が0以下となるなら、終了としてポーリングも停止したいです。そうするには、ブレードを編集して、0秒以下となったら、ポーリングの代わりに「終了しました!」の表示を条件文と使って入れ替えます。簡単ですね。

<div>
    @if ($remaining <= 0)
        <div class="text-red-600 font-bold">終了しました!</div>
    @else
        <div class="font-bold" wire:poll.1s>
            残りの秒数:{{ $remaining }}
        </div>
    @endif
</div>

以下のようになりました。

最後に

Livewireを使うと実に簡単にポーリングができてしまいます、しかし、ポーリングはこのデモでは毎秒サーバーにリクエストするので、ユーザー数が多くなるとサーバーにはかなりな負荷となります。スケールが小さめのプロジェクト向きと思ってください。スケールが大きいプロジェクトではウェブソケットの使用を考えてください。

ウェブソケットに関しては、以下のご参照を。

会員チャットの解説(1)Websocket

メルマガ購読の申し込みはこちらから。

By khino