イントロダクションIntroduction
Laravelのイベント機能はオブザーバーのシンプルな実装を提供します。アプリケーションの中でイベントを発行し、購読するために使用します。イベントクラスは通常app/Events
ディレクトリーへ保存され、リスナーはapp/Listener
に設置します。Laravel's events provides a simple observer implementation, allowing you to subscribe and listen for events in your application. Event classes are typically stored in the app/Events
directory, while their listeners are stored in app/Listeners
.
イベント/リスナー登録Registering Events / Listeners
Laravelアプリケーションに含まれているEventServiceProvider
は、イベントハンドラーを全て登録するために便利な手段を提供しています。listen
プロパティは全イベント(キー)とリスナー(値)で構成されている配列です。もちろん、アプリケーションで必要とされているイベントをこの配列に好きなだけ追加できます。たとえばPodcastWasPurchased
イベントを追加してみましょう。The EventServiceProvider
included with your Laravel application provides a convenient place to register all event listeners. The listen
property contains an array of all events (keys) and their listeners (values). Of course, you may add as many events to this array as your application requires. For example, let's add our PodcastWasPurchased
event:
/**
* アプリケーションのイベントリスナーをマップ
*
* @var array
*/
protected $listen = [
'App\Events\PodcastWasPurchased' => [
'App\Listeners\EmailPurchaseConfirmation',
],
];
イベントとリスナークラス生成Generating Event / Listener Classes
もちろん毎回ハンドラーやリスナーを作成するのは手間がかかります。代わりにハンドラーとリスナーをEventServiceProvider
に追加し、event:generate
コマンドを使いましょう。このコマンドはEventServiceProvider
にリストしてあるイベントやリスナーを生成してくれます。既存のイベントとハンドラーには当然変更を加えません。Of course, manually creating the files for each event and listener is cumbersome. Instead, simply add listeners and events to your EventServiceProvider
and use the event:generate
command. This command will generate any events or listeners that are listed in your EventServiceProvider
. Of course, events and listeners that already exist will be left untouched:
php artisan event:generate
イベントを自分で登録するRegistering Events Manually
通常、イベントはEventServiceProvider
の$listen
配列を使用し登録すべきです。しかし、Event
ファサードやIlluminate\Contracts\Events\Dispatcher
契約の実装を使用し、イベントディスパッチャーでイベントを自分で登録することもできます。Typically, events should be registered via the EventServiceProvider
$listen
array; however, you may also register events manually with the event dispatcher using either the Event
facade or the Illuminate\Contracts\Events\Dispatcher
contract implementation:
/**
* アプリケーションのその他のイベントを登録
*
* @param \Illuminate\Contracts\Events\Dispatcher $events
* @return void
*/
public function boot(DispatcherContract $events)
{
parent::boot($events);
$events->listen('event.name', function ($foo, $bar) {
//
});
}
ワイルドカードイベントリスナーWildcard Event Listeners
*
をワイルドカードとしてリスナーを登録することができ、同じリスナーで複数のイベントを補足することが可能です。ワイルドカードリスナーは一つの引数により、イベントデータ全体の配列を受け取ります。You may even register listeners using the *
as a wildcard, allowing you to catch multiple events on the same listener. Wildcard listeners receive the entire event data array as a single argument:
$events->listen('event.*', function (array $data) {
//
});
イベント定義Defining Events
イベントクラスはシンプルなデータコンテナで、イベントに関する情報を保持します。たとえば生成したPodcastWasPurchased
イベントがEloquent ORMオブジェクトを受け取るとしましょう。An event class is simply a data container which holds the information related to the event. For example, let's assume our generated PodcastWasPurchased
event receives an Eloquent ORM[/docs/{{version}}/eloquent] object:
<?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
トレイトが優雅にシリアライズします。As you can see, this event class contains no special logic. It is simply a container for the Podcast
object that was purchased. The SerializesModels
trait used by the event will gracefully serialize any Eloquent models if the event object is serialized using PHP's serialize
function.
リスナーの定義Defining Listeners
次にサンプルイベントのリスナーを取り上げましょう。イベントリスナーはイベントインスタンスをhandle
メソッドで受け取ります。event:generate
コマンドは自動的に対応するイベントクラスをインポートし、handle
メソッドのイベントのタイプヒントを行います。そのイベントに対応するために必要なロジックを実行してください。Next, let's take a look at the listener for our example event. Event listeners receive the event instance in their handle
method. The event:generate
command will automatically import the proper event class and type-hint the event on the handle
method. Within the handle
method, you may perform any logic necessary to respond to the event.
<?php
namespace App\Listeners;
use App\Events\PodcastWasPurchased;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class EmailPurchaseConfirmation
{
/**
* イベントリスナー生成
*
* @return void
*/
public function __construct()
{
//
}
/**
* イベントの処理
*
* @param PodcastWasPurchased $event
* @return void
*/
public function handle(PodcastWasPurchased $event)
{
// $event->podcastでポッドキャストへアクセス…
}
}
イベントリスナーは必要な依存をコンストラクターのタイプヒントで指定できます。イベントリスナーは全てLaravelのサービスコンテナで依存解決されるので、依存は自動的に注入されます。Your event listeners may also type-hint any dependencies they need on their constructors. All event listeners are resolved via the Laravel service container[/docs/{{version}}/container], so dependencies will be injected automatically:
use Illuminate\Contracts\Mail\Mailer;
public function __construct(Mailer $mailer)
{
$this->mailer = $mailer;
}
イベントの伝播の停止Stopping The Propagation Of An Event
場合によりイベントが他のリスナーへ伝播されるのを止めたいこともあります。その場合はhandle
メソッドからfalse
を返してください。Sometimes, you may wish to stop the propagation of an event to other listeners. You may do so by returning false
from your listener's handle
method.
イベントリスナーのキュー投入Queued Event Listeners
イベントハンドラーをキューに投入する必要があるのですか? これ以上ないくらい簡単です。リスナークラスにShouldQueue
インターフェイスを追加するだけです。リスナーがevent:generate
Artisanコマンドにより生成されている場合は現在の名前空間にこのインターフェイスがインポートされていますので、すぐに使えます。Need to queue[/docs/{{version}}/queues] an event listener? It couldn't be any easier. Simply add the ShouldQueue
interface to the listener class. Listeners generated by the event:generate
Artisan command already have this interface imported into the current namespace, so you can use it immediately:
<?php
namespace App\Listeners;
use App\Events\PodcastWasPurchased;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class EmailPurchaseConfirmation implements ShouldQueue
{
//
}
これだけです!これでこのハンドラーがイベントのために呼び出されると、Laravelのキューシステムを使い、イベントデスパッチャーにより自動的にキューへ投入されます。キューにより実行されるリスナーから例外が投げられなければ、そのキュージョブは処理が済んだら自動的に削除されます。That's it! Now, when this listener is called for an event, it will be queued automatically by the event dispatcher using Laravel's queue system[/docs/{{version}}/queues]. If no exceptions are thrown when the listener is executed by the queue, the queued job will automatically be deleted after it has processed.
キューへのアクセスManually Accessing The Queue
裏で動作しているキュージョブのdelete
やrelease
メソッドを直接呼び出したければ可能です。生成されたリスナーではデフォルトでインポートされているIlluminate\Queue\InteractsWithQueue
トレイトを呼び出してください。両メソッドへのアクセスを提供します。If you need to access the underlying queue job's delete
and release
methods manually, you may do so. The Illuminate\Queue\InteractsWithQueue
trait, which is imported by default on generated listeners, gives you access to these methods:
<?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);
}
}
}
イベント発行Firing Events
イベントを発行するにはEvent
ファサードを使用し、fire
メソッドにイベントのインスタンスを渡してください。fire
メソッドはそのイベントを登録されているリスナー全部へディスパッチします。To fire an event, you may use the Event
facade[/docs/{{version}}/facades], passing an instance of the event to the fire
method. The fire
method will dispatch the event to all of its registered listeners:
<?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
ヘルパ関数でイベントを発行します。Alternatively, you may use the global event
helper function to fire events:
event(new PodcastWasPurchased($podcast));
ブロードキャストイベントBroadcasting Events
多くの近代的なアプリケーションでは、リアルタイムでライブ更新されるユーザーインターフェイスを実装するために、Webソケットを利用しています。サーバーであるデータが更新されたら、処理するクライアントへWebソケット接続を通じて送られます。In many modern web applications, web sockets are used to implement real-time, live-updating user interfaces. When some data is updated on the server, a message is typically sent over a websocket connection to be handled by the client.
こうしたタイプのアプリケーション構築を援助するため、LaravelはイベントをWebソケット接続を通じて簡単に「ブロードキャスト」できます。Laravelのイベントをブロードキャストすることで、サーバーサイドのコードとクライアントサイドのJavaScriptで同じ名前のイベントを共有できるようになります。To assist you in building these types of applications, Laravel makes it easy to "broadcast" your events over a websocket connection. Broadcasting your Laravel events allows you to share the same event names between your server-side code and your client-side JavaScript framework.
設定Configuration
イベントブロードキャストの設定オプションはconfig/broadcasting.php
設定ファイルの中にあります。Laravelはいくつかのドライバーを用意しており、PusherやRedis、それにローカルの開発とデバッグのためのlog
ドライバーがあります。All of the event broadcasting configuration options are stored in the config/broadcasting.php
configuration file. Laravel supports several broadcast drivers out of the box: Pusher[https://pusher.com], Redis[/docs/{{version}}/redis], and a log
driver for local development and debugging. A configuration example is included for each of these drivers.
ブロードキャスト事前要件Broadcast Prerequisites
以下の依存パッケージがイベントのブロードキャストに必要です。The following dependencies are needed for event broadcasting:
- Pusher:
pusher/pusher-php-server ~2.0
Pusher:pusher/pusher-php-server ~2.0
- Redis:
predis/predis ~1.0
Redis:predis/predis ~1.0
動作要件:キューQueue Prerequisites
イベントをブロードキャストする前に、キューリスナーを設定し動かしておく必要があります。全てのイベントブロードキャストはキュー投入されるジョブとして動きますので、アプリケーションの反応時間にシリアスな影響を与えません。Before broadcasting events, you will also need to configure and run a queue listener[/docs/{{version}}/queues]. All event broadcasting is done via queued jobs so that the response time of your application is not seriously affected.
ブロードキャストイベント作成Marking Events For Broadcast
Laravelにイベントがブロードキャストされることを知らせるためにIlluminate\Contracts\Broadcasting\ShouldBroadcast
インターフェイスを実装してください。ShouldBroadcast
インターフェイスはbroadcastOn
メソッドの実装ひとつだけを求めます。broadcastOn
メソッドはブロードキャストされる「チャンネル」名の配列を返す必要があります。To inform Laravel that a given event should be broadcast, implement the Illuminate\Contracts\Broadcasting\ShouldBroadcast
interface on the event class. The ShouldBroadcast
interface requires you to implement a single method: broadcastOn
. The broadcastOn
method should return an array of "channel" names that the event should be broadcast on:
<?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];
}
}
それから、通常通りにイベントを発行するだけです。イベントが発行されるとキュージョブで指定されたブロードキャストドライバーにより自動的にブロードキャストされます。Then, you only need to fire the event[#firing-events] as you normally would. Once the event has been fired, a queued job[/docs/{{version}}/queues] will automatically broadcast the event over your specified broadcast driver.
ブロードキャストイベント名のオーバーライドOverriding Broadcast Event Name
ブロードキャストイベント名は、デフォルトでそのイベントの完全な名前空間名になります。上の例のクラスでは、ブロードキャストイベントは、App\Events\ServerCreated
になります。broadcastAs
メソッドにより、このブロードキャストイベント名を好きなようにカスタマイズできます。By default, the broadcast event name will be the fully qualified class name of the event. Using the example class above, the broadcast event would be App\Events\ServerCreated
. You can customize this broadcast event name to whatever you want using the broadcastAs
method:
/**
* ブロードキャストイベント名の取得
*
* @return string
*/
public function broadcastAs()
{
return 'app.server-created';
}
ブロードキャストデータBroadcast Data
イベントをブロードキャストする時、全public
プロパティーは自動的にシリアライズされ、イベントの本体(ペイロード)としてブロードキャストされます。それによりJavaScriptアプリケーションでパブリックデータへアクセスできるようになります。たとえばイベントにEloquentモデルのpublicの$user
プロパティがあったとすると、ブロードキャストの本体は次のようになります。When an event is broadcast, all of its public
properties are automatically serialized and broadcast as the event's payload, allowing you to access any of its public data from your JavaScript application. So, for example, if your event has a single public $user
property that contains an Eloquent model, the broadcast payload would be:
{
"user": {
"id": 1,
"name": "Jonathan Banks"
...
}
}
しかしブロードキャストされる本体をより良く調整、コントロールしたければ、broadcastWith
メソッドをイベントに追加してください。このメソッドは、そのイベントでブロードキャストしたいデータの配列を返す必要があります。However, if you wish to have even more fine-grained control over your broadcast payload, you may add a broadcastWith
method to your event. This method should return the array of data that you wish to broadcast with the event:
/**
* ブロードキャストするデータ取得
*
* @return array
*/
public function broadcastWith()
{
return ['user' => $this->user->id];
}
イベントブロードキャストの利用Consuming Event Broadcasts
PusherPusher
PusherのJacaScript SDKを使い、Pusherドライバーでイベントブロードキャストを便利に使いたいと思うでしょう。たとえば前例のApp\Events\ServerCreated
イベントを使ってみましょう。You may conveniently consume events broadcast using the Pusher[https://pusher.com] driver using Pusher's JavaScript SDK. For example, let's consume the App\Events\ServerCreated
event from our previous examples:
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);
});
RedisRedis
Redisブロードキャストを使用する場合、メッセージを受け取るために自分でRedisのpub/subコンシューマーを書く必要があり、自分で選んだWebソケットのテクノロジーを使いブロードキャストしなくてはなりません。たとえばNodeで書かれ人気のあるSocket.ioライブラリーを使うことを選択できます。If you are using the Redis broadcaster, you will need to write your own Redis pub/sub consumer to receive the messages and broadcast them using the websocket technology of your choice. For example, you may choose to use the popular Socket.io[http://socket.io] library which is written in Node.
socket.io
とioredis
Nodeライブラリーを使い、Laravelアプリケーションからブロードキャストされた全イベントを発行するイベントブロードキャスターを簡単に書けます。Using the socket.io
and ioredis
Node libraries, you can quickly write an event broadcaster to publish all events that are broadcast by your Laravel application:
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);
});
イベント購読Event Subscribers
イベント購読クラスは一つのクラスで多くのイベントを購読するためのものです。購読クラスはイベントデスパッチャーインスタンスが渡されるsubscribe
メソッドを実装しなくてはなりませんEvent subscribers are classes that may subscribe to multiple events from within the class itself, allowing you to define several event handlers within a single class. Subscribers should define a subscribe
method, which will be passed an event dispatcher instance:
<?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'
);
}
}
イベント購読クラスの登録Registering An Event Subscriber
購読クラスを定義したらイベントディスパッチャーに登録します。EventServiceProvider
の$subscribe
プロパティを使い、後続クラスを登録します。たとえばUserEventListener
を追加してみましょう。Once the subscriber has been defined, it may be registered with the event dispatcher. You may register subscribers using the $subscribe
property on the EventServiceProvider
. For example, let's add the 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',
];
}
フレームワークのイベントFramework Events
Laravelはフレームワークが実行するアクションに対して発行する、様々な「コア」イベントを提供しています。皆さんが独自に用意するカスタムイベントと同じ方法で、これらのコアイベントも購入できます。Laravel provides a variety of "core" events for actions performed by the framework. You can subscribe to them in the same way that you subscribe to your own custom events:
イベントEvent | パラメータParameter(s) |
---|---|
artisan.startartisan.start | $application$application |
auth.attemptauth.attempt | $credentials, $remember, $login$credentials, $remember, $login |
auth.loginauth.login | $user, $remember$user, $remember |
auth.logoutauth.logout | $user$user |
cache.missedcache.missed | $key$key |
cache.hitcache.hit | $key, $value$key, $value |
cache.writecache.write | $key, $value, $minutes$key, $value, $minutes |
cache.deletecache.delete | $key$key |
connection.{name}.beganTransactionconnection.{name}.beganTransaction | $connection$connection |
connection.{name}.committedconnection.{name}.committed | $connection$connection |
connection.{name}.rollingBackconnection.{name}.rollingBack | $connection$connection |
illuminate.queryilluminate.query | $query, $bindings, $time, $connectionName$query, $bindings, $time, $connectionName |
illuminate.queue.afterilluminate.queue.after | $connection, $job, $data$connection, $job, $data |
illuminate.queue.failedilluminate.queue.failed | $connection, $job, $data$connection, $job, $data |
illuminate.queue.stoppingilluminate.queue.stopping | nullnull |
mailer.sendingmailer.sending | $message$message |
router.matchedrouter.matched | $route, $request$route, $request |
composing:{view name}composing:{view name} | $view$view |
creating:{view name}creating:{view name} | $view$view |