Laravel イベント非同期にチャレンジしてみる。
使ってみた感じ
①イベントは同期で呼びだされる。
②イベントは非同期で呼び出すこともできる。これでイベントの使い道が見えてきました。
③EventServiceProvider.php で受け取るイベントとリスナーを定義してから、コマンドでイベントクラスとリスナークラスのファイルを生成する。
④1つのイベントを発火して、複数のリスナーに処理させることができるのが良いのかも。イベントに対するリスナーの変更も容易。
イベントで非同期処理ができるのは大きいですね。
手順
Laravel をインストールしてイベントを試す手順。
- プロジェクト名(event)を決めて以下のコマンドを実行します。
curl -s https://laravel.build/event | bash
インストール時にプロジェクト名のディレクトリが作成されます。
- インストールの最後に sudo でパスワードの入力を求められます。
↓下のメッセージが表示されてインストールは終わります。
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
Get started with: cd event && ./vendor/bin/sail up
- sail のエイリアスを定義します。
echo "alias sail='[ -f sail ] && sh sail || sh vendor/bin/sail'" >> ~/.bashrc
source ~/.bashrc
Laravel のインストールはここまで。
- 「sail up」でコンテナを起動します。
cd event && sail up -d
- app/Providers/EventServiceProvider.php にイベントを定義します。
↓下の OrderShippedクラス、SendShipmentNotificationクラス というのは未だ存在しないファイルです。これからコマンドでクラスファイルを生成する為に、EventServiceProvider.php ファイルに定義する感じです。
<?php
namespace App\Providers;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;
use App\Events\OrderShipped;
use App\Listeners\SendShipmentNotification1;
use App\Listeners\SendShipmentNotification2;
class EventServiceProvider extends ServiceProvider
{
/**
* The event to listener mappings for the application.
*
* @var array<class-string, array<int, class-string>>
*/
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
],
OrderShipped::class => [
SendShipmentNotification1::class,
SendShipmentNotification2::class,
],
];
/**
* Register any events for your application.
*
* @return void
*/
public function boot()
{
//
}
/**
* Determine if events and listeners should be automatically discovered.
*
* @return bool
*/
public function shouldDiscoverEvents()
{
return false;
}
}
- artisanコマンド で↑上の EventServiceProvider.phpファイル からイベントクラスとリスナークラスファイルを生成します。
sail artisan event:generate
app/Events/OrderShipped.php、app/Listeners/SendShipmentNotification1.php、app/Listeners/SendShipmentNotification2.php のファイルが生成されます。
app/Events/OrderShipped.php
<?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 OrderShipped
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}
app/Listeners/SendShipmentNotification1.php
<?php
namespace App\Listeners;
use App\Events\OrderShipped;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class SendShipmentNotification1
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param \App\Events\OrderShipped $event
* @return void
*/
public function handle(OrderShipped $event)
{
//
\Log::debug('デバッグ1');
}
}
app/Listeners/SendShipmentNotification2.php
<?php
namespace App\Listeners;
use App\Events\OrderShipped;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class SendShipmentNotification2
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param \App\Events\OrderShipped $event
* @return void
*/
public function handle(OrderShipped $event)
{
//
\Log::debug('デバッグ2');
}
}
- イベントを発火する
welcomページ 呼び出し時にイベントを発火するように routes/web.php を修正します。
<?php
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', function () {
// イベントを発火する。
event(new App\Events\OrderShipped);
\Log::debug('デバッグ');
return view('welcome');
});
ブラウザで画面を表示してログを確認します。↓下のようにイベントは同期で呼び出されました。
$ cat storage/logs/laravel.log
[2022-10-05 01:31:16] local.DEBUG: デバッグ1
[2022-10-05 01:31:16] local.DEBUG: デバッグ2
[2022-10-05 01:31:16] local.DEBUG: デバッグ
イベントを非同に期対応させます
- .env ファイルを QUEUE_CONNECTION を sync から database に変更します。
QUEUE_CONNECTION=database
- キューテーブルを作成します。
sail artisan queue:table
sail artisan migrate
- app/Listeners/SendShipmentNotification2.php に「ShouldQueue」を implements で非同期対応します。
implements ShouldQueue
<?php
namespace App\Listeners;
use App\Events\OrderShipped;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class SendShipmentNotification2 implements ShouldQueue
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param \App\Events\OrderShipped $event
* @return void
*/
public function handle(OrderShipped $event)
{
//
\Log::debug('デバッグ2');
}
}
storage/logs/laravel.log を確認すると非同期にした SendShipmentNotification2.php が呼び出されません。
[2022-10-05 02:13:44] local.DEBUG: デバッグ1
[2022-10-05 02:13:44] local.DEBUG: デバッグ
- 以下のコマンドを実行するとキューにたまったイベントが処理されます。
sail artisan queue:work
[2022-10-05 02:18:11] local.DEBUG: デバッグ1
[2022-10-05 02:18:11] local.DEBUG: デバッグ
[2022-10-05 02:18:13] local.DEBUG: デバッグ2
以上