Webアプリケーション的にイベントを説明すればそれは様々なアクションのことであり、ユーザー登録や商品購入、またはログイン・ログアウトなどのタイミングのことを指します。そして、それらアクションを感知し(トリガー)、そのタイミングに付随した処理をさせることができるのがリスナです。
例えばユーザー登録や商品が購入されたタイミングでメールを送信させるなどの処理は、Laravelのイベント/リスナの機能に沿って構築すれば、コードを分離させ効率よく開発することができます。
JavaScriptにおけるイベント/リスナ(イベントリスナー)については下記ページをご覧下さい。
イベントハンドラー/イベントリスナーについて[addEventListener]
本記事ではイベント/リスナを利用して、ユーザー登録時にメールを送信し、同時にログを残す機能を実装してみます。またShouldQueueインターフェイスによるメール送信の非同期処理を実行してみます。
はじめに、ユーザーの新規登録時にメールを送信する機能を実装します。Laravel(本記事ではFramework 7.14.1を利用)に備わっている認証(Auth)機能を利用します。この構築手順は本記事では説明しませんので関連ページをご覧下さい。
関連ページ
mailableクラスの作成・mailtrapサービスの利用
まずメール送信機能を構築します。イベント・リスナ自体の説明とは関係ないので本記事では手順のみを記述していきます。mailableクラスの作成、また本記事ではメールの送信先として mailtrap サービスを利用します。それら説明は下記関連ページをご覧下さい。
関連ページ
mailableクラスを作成します。
1 |
$ php artisan make:mail HelloEmail |
Mailディレクトリ・HelloEmail.phpファイルが生成されるので下記のように編集します。
app/Mail/HelloEmail.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 |
<?php namespace App\Mail; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Mail\Mailable; use Illuminate\Queue\SerializesModels; class HelloEmail extends Mailable { use Queueable, SerializesModels; public $user; /** * Create a new message instance. * * @return void */ public function __construct($user) { $this->user = $user; } /** * Build the message. * * @return $this */ public function build() { return $this->view('emails.contact') ->subject('登録されました') ->from('admin@hoge.co.jp', '運営') ->with('user', $this->user); } } |
メールの中身となるテンプレートを作成します。
resources/views/emails/contact.blade.php
1 2 3 |
<p>名前:{{ $user['name'] }}さん</p> <p>メールアドレス:{{ $user['email'] }}</p> <p>上記で登録されました</p> |
.envファイルにmailtrapサービスで取得したUsernameとPasswordを設定します。※.envを編集したらキャッシュのクリアを忘れないで下さい。
.env
1 2 3 4 5 6 7 8 |
MAIL_MAILER=smtp MAIL_HOST=smtp.mailtrap.io MAIL_PORT=2525 MAIL_USERNAME=○○○○○○○○○○ MAIL_PASSWORD=○○○○○○○○○○ MAIL_ENCRYPTION=null MAIL_FROM_ADDRESS=null MAIL_FROM_NAME="${APP_NAME}" |
イベント/リスナの登録と作成
EventServiceProvider.phpファイルにイベントとリスナを登録します。
app/Providers/EventServiceProvider.php
1 2 3 4 5 |
protected $listen = [ 'App\Events\UserRegistered' => [ 'App\Listeners\SendEmail', ], ]; |
UserRegisteredという名前のイベント、SendEmailという名前のリスナを、キーと値の関係において設定します。
下記コマンドでEventsディレクトリとUserRegistered.php、ListenersディレクトリとSendEmail.phpが生成されます。
1 |
$ php artisan event:generate |
それぞれ下記のように編集します。
app/Events/UserRegistered.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 |
<?php namespace App\Events; use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PresenceChannel; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; class UserRegistered { use Dispatchable, InteractsWithSockets, SerializesModels; public $user; /** * Create a new event instance. * * @return void */ public function __construct($user) { $this->user = $user; } /** * Get the channels the event should broadcast on. * * @return \Illuminate\Broadcasting\Channel|array */ public function broadcastOn() { return new PrivateChannel('channel-name'); } } |
23行目 event関数(後述)によるイベント発行時の引数を受け取ります。イベントクラスでは利用するデータを保持します。
app/Listeners/SendEmail.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 |
<?php namespace App\Listeners; use App\Events\UserRegistered; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Queue\InteractsWithQueue; use App\Mail\HelloEmail; use Mail; class SendEmail { /** * Create the event listener. * * @return void */ public function __construct() { // } /** * Handle the event. * * @param UserRegistered $event * @return void */ public function handle(UserRegistered $event) { Mail::to($event->user->email)->send(new HelloEmail($event->user)); } } |
実行する処理は、handleメソッドに記述します。
ユーザー登録のコントローラにおいて、ユーザー登録されたタイミングで、イベントを発行させます。
app/Http/Controllers/Auth/RegisterController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
use App\Events\UserRegistered; 〜省略 protected function create(array $data) { $user = User::create([ 'name' => $data['name'], 'email' => $data['email'], 'password' => Hash::make($data['password']), ]); event(new UserRegistered($user)); return $user; } |
13行目 event関数で、UserRegisteredイベントを発行しています。引数には登録されたユーザー情報を指定します。
以上でユーザー登録の際にメールが送信されるようになります。しかし、「Register」ボタンをクリックしてからログインするまで(Dashboard画面へ移行する間)、少し時間がかかることが確認できます。実際にメール送信の処理に時間がかかってしまうからです。次の項目ではこのメール送信の部分(リスナ)をキュー(Queue)の機能を利用して、非同期処理に置き換えてみます。
リスナのキュー(Queue)投入による非同期処理
キュー(Queue)自体に関する説明は下記関連ページをご覧下さい。本記事ではキュードライバとしてデータベース(MySQL)を利用します。
まずはキューの設定をおこないます。
.envファイルのQUEUE_CONNECTIONをsyncからdatabaseに変更します。※.envを編集したらキャッシュのクリアを忘れないで下さい。
1 |
QUEUE_CONNECTION=database |
リスナを保存するためのjobsテーブルのマイグレーションファイルを作成します。
1 |
$ php artisan queue:table |
マイグレーションを実行します。
1 |
$ php artisan migrate |
キューワーカを起動します。
1 |
$ php artisan queue:work |
作成したSendEmailリスナに、ShouldQueueインターフェイスを追加します。
1 |
class SendEmail implements ShouldQueue |
以上でリスナをキューに投入できるようになり、メール送信が非同期処理されるようになります。実際にユーザー登録画面で「Register」ボタンをクリックすると、すぐにDashboard画面へと遷移することが確認できます。
リスナの追加
一つのイベントに対して複数のリスナーを紐付けることも可能です。単純にログを出力させるリスナを追加してみます。
app/Providers/EventServiceProvider.php
1 2 3 4 5 6 |
protected $listen = [ 'App\Events\UserRegistered' => [ 'App\Listeners\SendEmail', 'App\Listeners\LogName', ], ]; |
UserRegisteredイベントに対して、LogNameリスナを追加しています。
下記コマンドでLogName.phpファイルが新たに生成されます。既に生成済のイベントとリスナのファイルの内容は、変更されません。
1 |
$ php artisan event:generate |
下記のように編集します。
app/Listeners/LogName.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 |
<?php namespace App\Listeners; use App\Events\UserRegistered; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Support\Facades\Log; class LogName { /** * Create the event listener. * * @return void */ public function __construct() { // } /** * Handle the event. * * @param UserRegistered $event * @return void */ public function handle(UserRegistered $event) { Log::info($event->user); } } |
実際に新規ユーザー登録をすると、メール送信されると同時に、laravel.logファイルにユーザー情報が書き込まれているのが確認できます。
storage/logs/laravel.log
1 |
[2020-06-07 15:13:49] local.INFO: {"name":"hanako","email":"hanako@hoge.co.jp","updated_at":"2020-06-07T15:13:49.000000Z","created_at":"2020-06-07T15:13:49.000000Z","id":2} |
参照ページ