Laravel 5.2 イベント

イントロダクション

Laravelのイベント機能はオブザーバーのシンプルな実装を提供します。アプリケーションの中でイベントを発行し、購読するために使用します。イベントクラスは通常app/Eventsディレクトリへ保存され、リスナはapp/Listenerに設置します。

イベント/リスナ登録

Laravelアプリケーションに含まれているEventServiceProviderは、イベントリスナを全て登録するために便利な場所を提供しています。listenプロパティは全イベント(キー)とリスナ(値)で構成されている配列です。もちろん、アプリケーションで必要とされているイベントをこの配列に好きなだけ追加できます。たとえばPodcastWasPurchasedイベントを追加してみましょう。

/**
 * アプリケーションのイベントリスナをマップ
 *
 * @var array
 */
protected $listen = [
    'App\Events\PodcastWasPurchased' => [
        'App\Listeners\EmailPurchaseConfirmation',
    ],
];

イベントとリスナクラス生成

もちろん毎回ハンドラやリスナを作成するのは手間がかかります。代わりにハンドラとリスナをEventServiceProviderに追加し、event:generateコマンドを使いましょう。このコマンドはEventServiceProviderにリストしてあるイベントやリスナを生成してくれます。既存のイベントとハンドラには当然変更を加えません。

php artisan event:generate

イベントを自分で登録する

通常、イベントはEventServiceProvider$listen配列を使用し登録すべきです。しかし、EventファサードやIlluminate\Contracts\Events\Dispatcher契約の実装を使用し、イベントディスパッチャーでイベントを自分で登録することもできます。

/**
 * アプリケーションのその他のイベントを登録
 *
 * @param  \Illuminate\Contracts\Events\Dispatcher  $events
 * @return void
 */
public function boot(DispatcherContract $events)
{
    parent::boot($events);

    $events->listen('event.name', function ($foo, $bar) {
        //
    });
}

ワイルドカードイベントリスナ

*をワイルドカードとしてリスナを登録することができ、同じリスナで複数のイベントを補足することが可能です。ワイルドカードリスナは一つの引数により、イベントデータ全体の配列を受け取ります。

$events->listen('event.*', function (array $data) {
    //
});

イベント定義

イベントクラスはシンプルなデータコンテナで、イベントに関する情報を保持します。たとえば生成したPodcastWasPurchasedイベントがEloquent ORMオブジェクトを受け取るとしましょう。

<?php

namespace App\Events;

use App\Podcast;
use App\Events\Event;
use Illuminate\Queue\SerializesModels;

class PodcastWasPurchased extends Event
{
    use SerializesModels;

    public $podcast;

    /**
     * 新しいイベントインスタンスの生成
     *
     * @param  Podcast  $podcast
     * @return void
     */
    public function __construct(Podcast $podcast)
    {
        $this->podcast = $podcast;
    }
}

ご覧の通り、このクラスはロジックを含みません。購入されたPodcastオブジェクトのための、単なるコンテナに過ぎません。イベントオブジェクトがPHPのserialize関数でシリアライズされる場合でも、EloquentモデルはSerializesModelsトレイトが優雅にシリアライズします。

リスナの定義

次にサンプルイベントのリスナを取り上げましょう。イベントリスナはイベントインスタンスをhandleメソッドで受け取ります。event:generateコマンドは自動的に対応するイベントクラスをインポートし、handleメソッドのイベントのタイプヒントを行います。そのイベントに対応するために必要なロジックを実行してください。

<?php

namespace App\Listeners;

use App\Events\PodcastWasPurchased;

class EmailPurchaseConfirmation
{
    /**
     * イベントリスナ生成
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * イベントの処理
     *
     * @param  PodcastWasPurchased  $event
     * @return void
     */
    public function handle(PodcastWasPurchased $event)
    {
        // $event->podcastにより、ポッドキャストへアクセス
    }
}

イベントリスナは必要な依存をコンストラクターのタイプヒントで指定できます。イベントリスナは全てLaravelのサービスコンテナで依存解決されるので、依存は自動的に注入されます。

use Illuminate\Contracts\Mail\Mailer;

public function __construct(Mailer $mailer)
{
    $this->mailer = $mailer;
}

イベントの伝播の停止

場合によりイベントが他のリスナへ伝播されるのを止めたいこともあります。その場合はhandleメソッドからfalseを返してください。

イベントリスナのキュー投入

イベントリスナをキューに投入する必要があるのですか? これ以上ないくらい簡単です。リスナクラスにShouldQueueインターフェイスを追加するだけです。リスナがevent:generate Artisanコマンドにより生成されている場合は、現在の名前空間下にこのインターフェイスがインポートされていますので、すぐに使用できます。

<?php

namespace App\Listeners;

use App\Events\PodcastWasPurchased;
use Illuminate\Contracts\Queue\ShouldQueue;

class EmailPurchaseConfirmation implements ShouldQueue
{
    //
}

これだけです!これでこのリスナがイベントのために呼び出されると、Laravelのキューシステムを使い、イベントデスパッチャーにより自動的にキューへ投入されます。キューにより実行されるリスナから例外が投げられなければ、そのキュージョブは処理が済んだら自動的に削除されます。

キューへのアクセス

裏で動作しているキュージョブのdeletereleaseメソッドを直接呼び出したければ可能です。生成されたリスナではデフォルトでインポートされているIlluminate\Queue\InteractsWithQueueトレイトを呼び出してください。両メソッドへのアクセスを提供します。

<?php

namespace App\Listeners;

use App\Events\PodcastWasPurchased;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

class EmailPurchaseConfirmation implements ShouldQueue
{
    use InteractsWithQueue;

    public function handle(PodcastWasPurchased $event)
    {
        if (true) {
            $this->release(30);
        }
    }
}

イベント発行

イベントを発行するにはEventファサードを使用し、fireメソッドにイベントのインスタンスを渡してください。fireメソッドはそのイベントを登録されているリスナ全部へディスパッチします。

<?php

namespace App\Http\Controllers;

use Event;
use App\Podcast;
use App\Events\PodcastWasPurchased;
use App\Http\Controllers\Controller;

class UserController extends Controller
{
    /**
     * 指定されたユーザーのプロフィール表示
     *
     * @param  int  $userId
     * @param  int  $podcastId
     * @return Response
     */
    public function purchasePodcast($userId, $podcastId)
    {
        $podcast = Podcast::findOrFail($podcastId);

        // ポッドキャスト購入ロジック…

        Event::fire(new PodcastWasPurchased($podcast));
    }
}

もしくは、グローバルなeventヘルパ関数でイベントを発行します。

event(new PodcastWasPurchased($podcast));

ブロードキャストイベント

多くの近代的なアプリケーションでは、リアルタイムでライブ更新されるユーザインターフェイスを実装するために、Webソケットを利用しています。サーバーであるデータが更新されたら、処理するクライアントへWebソケット接続を通じて送られます。

こうしたタイプのアプリケーション構築を援助するため、LaravelはイベントをWebソケット接続を通じて簡単に「ブロードキャスト」できます。Laravelのイベントをブロードキャストすることで、サーバーサイドのコードとクライアントサイドのJavaScriptで同じ名前のイベントを共有できるようになります。

設定

イベントブロードキャストの設定オプションはconfig/broadcasting.php設定ファイルの中にあります。Laravelはいくつかのドライバを用意しており、PusherRedis、それにローカルの開発とデバッグのためのlogドライバがあります。

ブロードキャスト事前要件

以下の依存パッケージがイベントのブロードキャストに必要です。

動作要件:キュー

イベントをブロードキャストする前に、キューリスナを設定し動かしておく必要があります。全てのイベントブロードキャストはキュー投入されるジョブとして動きますので、アプリケーションの反応時間にシリアスな影響を与えません。

ブロードキャストイベント作成

Laravelにイベントがブロードキャストされることを知らせるためにIlluminate\Contracts\Broadcasting\ShouldBroadcastインターフェイスを実装してください。ShouldBroadcastインターフェイスはbroadcastOnメソッドの実装ひとつだけを求めます。broadcastOnメソッドはブロードキャストされる「チャンネル」名の配列を返す必要があります。

<?php

namespace App\Events;

use App\User;
use App\Events\Event;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class ServerCreated extends Event implements ShouldBroadcast
{
    use SerializesModels;

    public $user;

    /**
     * イベントインスタンスの生成
     *
     * @return void
     */
    public function __construct(User $user)
    {
        $this->user = $user;
    }

    /**
     * イベントをブロードキャストするチャンネル取得
     *
     * @return array
     */
    public function broadcastOn()
    {
        return ['user.'.$this->user->id];
    }
}

それから、通常通りにイベントを発行するだけです。イベントが発行されるとキュージョブで指定されたブロードキャストドライバにより自動的にブロードキャストされます。

ブロードキャストデータ

イベントをブロードキャストする時、全publicプロパティーは自動的にシリアライズされ、イベントの本体(ペイロード)としてブロードキャストされます。それによりJavaScriptアプリケーションでパブリックデータへアクセスできるようになります。たとえばイベントにEloquentモデルのpublicの$userプロパティがあったとすると、ブロードキャストの本体は次のようになります。

{
    "user": {
        "id": 1,
        "name": "Jonathan Banks"
        ...
    }
}

しかしブロードキャストされる本体をより良く調整、コントロールしたければ、broadcastWithメソッドをイベントに追加してください。このメソッドは、そのイベントでブロードキャストしたいデータの配列を返す必要があります。

/**
 * ブロードキャストするデータ取得
 *
 * @return array
 */
public function broadcastWith()
{
    return ['user' => $this->user->id];
}

イベントブロードキャストカスタマイズ

イベント名のカスタマイズ

ブロードキャストイベント名はデフォルトで、そのイベントの完全なクラス名となっています。ですから、イベントのクラス名がApp\Events\ServerCreatedならば、ブロードキャストイベントはApp\Events\ServerCreatedになります。イベントクラスのbroadcastAsメソッドで定義すれば、このブロードキャストイベント名をカスタマイズ可能です。

/**
 * ブロードキャストイベント名の取得
 *
 * @return string
 */
public function broadcastAs()
{
    return 'app.server-created';
}

キューのカスタマイズ

デフォルトで、ブロードキャストされるイベントは、queue.php設定ファイルのデフォルトキュー接続のデフォルトキューへ投入されます。イベントクラスへonQueueメソッドを追加すれば、イベントブロードキャスで使用されるキューをカスタマイズできます。このメソッドは、使用するキュー名を返します。

 /**
 * イベントが投入されるキュー名
 *
 * @return string
 */
public function onQueue()
{
    return 'your-queue-name';
}

イベントブロードキャストの利用

Pusher

PusherのJavaScript SDKを使い、Pusherドライバでイベントブロードキャストを便利に使いたいと思うでしょう。たとえば前例のApp\Events\ServerCreatedイベントを使ってみましょう。

this.pusher = new Pusher('pusher-key');

this.pusherChannel = this.pusher.subscribe('user.' + USER_ID);

this.pusherChannel.bind('App\\Events\\ServerCreated', function(message) {
    console.log(message.user);
});

Redis

Redisブロードキャストを使用する場合、メッセージを受け取るために自分でRedisのpub/subコンシューマーを書く必要があり、自分で選んだWebソケットのテクノロジーを使いブロードキャストしなくてはなりません。たとえばNodeで書かれ人気のあるSocket.ioライブラリーを使うことを選択できます。

socket.ioioredis Nodeライブラリーを使い、Laravelアプリケーションからブロードキャストされた全イベントを発行するイベントブロードキャスターを簡単に書けます。

var app = require('http').createServer(handler);
var io = require('socket.io')(app);

var Redis = require('ioredis');
var redis = new Redis();

app.listen(6001, function() {
    console.log('Server is running!');
});

function handler(req, res) {
    res.writeHead(200);
    res.end('');
}

io.on('connection', function(socket) {
    //
});

redis.psubscribe('*', function(err, count) {
    //
});

redis.on('pmessage', function(subscribed, channel, message) {
    message = JSON.parse(message);
    io.emit(channel + ':' + message.event, message.data);
});

イベント購読

イベント購読クラスは一つのクラスで多くのイベントを購読するためのものです。購読クラスはイベントデスパッチャーインスタンスが渡されるsubscribeメソッドを実装しなくてはなりません

<?php

namespace App\Listeners;

class UserEventListener
{
    /**
     * ユーザーログインイベントの処理
     */
    public function onUserLogin($event) {}

    /**
     * ユーザーログアウトイベントの処理
     */
    public function onUserLogout($event) {}

    /**
     * イベント購読リスナの登録
     *
     * @param  Illuminate\Events\Dispatcher  $events
     */
    public function subscribe($events)
    {
        $events->listen(
            'App\Events\UserLoggedIn',
            'App\Listeners\UserEventListener@onUserLogin'
        );

        $events->listen(
            'App\Events\UserLoggedOut',
            'App\Listeners\UserEventListener@onUserLogout'
        );
    }

}

イベント購読クラスの登録

購読クラスを定義したらイベントディスパッチャーに登録します。EventServiceProvider$subscribeプロパティを使い、後続クラスを登録します。たとえばUserEventListenerを追加してみましょう。

<?php

namespace App\Providers;

use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

class EventServiceProvider extends ServiceProvider
{
    /**
     * アプリケーションのイベントリスナをマップ
     *
     * @var array
     */
    protected $listen = [
        //
    ];

    /**
     * 登録する購読クラス
     *
     * @var array
     */
    protected $subscribe = [
        'App\Listeners\UserEventListener',
    ];
}

ドキュメント章別ページ

ヘッダー項目移動

注目:アイコン:ページ内リンク設置(リンクがないヘッダーへの移動では、リンクがある以前のヘッダーのハッシュをURLへ付加します。

移動

クリックで即時移動します。

設定

適用ボタンクリック後に、全項目まとめて適用されます。

カラーテーマ
和文指定 Pagination
和文指定 Scaffold
Largeスクリーン表示幅
インデント
本文フォント
コードフォント
フォント適用確認

フォントの指定フィールドから、フォーカスが外れると、当ブロックの内容に反映されます。EnglishのDisplayもPreviewしてください。

フォント設定時、表示に不具合が出た場合、当サイトのクッキーを削除してください。

バックスラッシュを含むインライン\Code\Blockの例です。

以下はコードブロックの例です。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * ユーザに関連する電話レコードを取得
     */
    public function phone()
    {
        return $this->hasOne('App\Phone');
    }
}

設定を保存する前に、表示が乱れないか必ず確認してください。CSSによるフォントファミリー指定の知識がない場合は、フォントを変更しないほうが良いでしょう。

キーボード・ショートカット

オープン操作

PDC

ページ(章)移動の左オフキャンバスオープン

HA

ヘッダー移動モーダルオープン

MS

移動/設定の右オフキャンバスオープン

ヘッダー移動

T

最初のヘッダーへ移動

E

最後のヘッダーへ移動

NJ

次ヘッダー(H2〜H4)へ移動

BK

前ヘッダー(H2〜H4)へ移動

その他

?

このヘルプページ表示
閉じる