Laravel イベント非同期にチャレンジしてみる。

ショコラ
ショコラ

Laravel イベント非同期にチャレンジしてみる。

使ってみた感じ

①イベントは同期で呼びだされる。
②イベントは非同期で呼び出すこともできる。これでイベントの使い道が見えてきました。
③EventServiceProvider.php で受け取るイベントとリスナーを定義してから、コマンドでイベントクラスとリスナークラスのファイルを生成する。
④1つのイベントを発火して、複数のリスナーに処理させることができるのが良いのかも。イベントに対するリスナーの変更も容易。

イベントで非同期処理ができるのは大きいですね。

もっさん先輩
もっさん先輩

手順

Laravel をインストールしてイベントを試す手順。

  1. プロジェクト名(event)を決めて以下のコマンドを実行します。
curl -s https://laravel.build/event | bash

インストール時にプロジェクト名のディレクトリが作成されます。

  1. インストールの最後に 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
  1. sail のエイリアスを定義します。
echo "alias sail='[ -f sail ] && sh sail || sh vendor/bin/sail'" >> ~/.bashrc
source ~/.bashrc

Laravel のインストールはここまで。

  1. 「sail up」でコンテナを起動します。
cd event && sail up -d
  1. 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;
    }
}
  1. 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');
    }
}
  1. イベントを発火する

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: デバッグ

イベントを非同に期対応させます

  1. .env ファイルを QUEUE_CONNECTION を sync から database に変更します。
QUEUE_CONNECTION=database
  1. キューテーブルを作成します。
sail artisan queue:table
sail artisan migrate
  1. 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: デバッグ
  1. 以下のコマンドを実行するとキューにたまったイベントが処理されます。
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

以上

Scroll to Top