Laravelでキューってみる

Laravelでキューを使ってみます。

キューです。FIFO。先入れ先出し。バックグラウンドで重たい処理をやらせたいときなどに便利なやつです。Laravelでも当然用意されています。今回そのキューでメール送信機能を実装しようと思います。Laravelのバージョンは5.7を使います。Laravelのキューのドライバーとしては、データベース、Redis、Amazon SQSなどがあるのですが、今回はデータベースのキューを使ってみようと思います。

まずLaravelのプロジェクト作成です。

```
$ composer create-project --prefer-dist laravel/laravel queue-sample
```

Valetでアクセスしてみます。

![スクリーンショット 2019-01-29 18.13.40.png](https://qiita-image-store.s3.amazonaws.com/0/123511/ab453eac-68b7-fed7-a67d-694fa69c3ed6.png)

ユーザ登録機能を作って、登録されたらキューを使って登録完了メールを送ってみます。
マイグレーションの前にデータベースを用意して、接続情報を`.env`に記述しておきましょう。

```
$ php artisan make:auth
$ php artisan migrate
```

![スクリーンショット 2019-01-29 18.25.06.png](https://qiita-image-store.s3.amazonaws.com/0/123511/0cad1ff3-144e-b6d8-ac2f-273c47475b5f.png)

画面右上に2つリンクが表示されるようになりました。
今回使うのは登録画面のほうです。

![スクリーンショット 2019-01-29 18.26.28.png](https://qiita-image-store.s3.amazonaws.com/0/123511/ab390c9a-9c4e-7657-6276-0fab72e29cd8.png)

この画面からユーザ登録出来ます。
登録は`app/Http/Controllers/Auth/RegisterController.php`内の`create()`が呼ばれます。
ここでreturnさせる前にメール送信キューにエンキューすればいいのではと思うわけです。

```app/Http/Controllers/Auth/RegisterController.php
    /**
     * Create a new user instance after a valid registration.
     *
     * @param  array  $data
     * @return \App\User
     */
    protected function create(array $data)
    {
        return User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => Hash::make($data['password']),
        ]);
    }
```

その前にメール送信処理のクラスも作ります。

```
$ php artisan make:mail UserRegistered --markdown=emails.user.registered
```

```app/Mail/UserRegistered.php
<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;

use App\User;

class UserRegistered extends Mailable
{
    use Queueable, SerializesModels;

    protected $user;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct(User $user)
    {
        $this->user = $user;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->markdown('emails.user.registered')
            ->subject('ご登録ありがとうございました')
            ->with(['user' => $this->user]);
    }
}
```

```resources/views/emails/user/registered.blade.php 
@component('mail::message')
{{ $user->name }} 様

ご登録ありがとうございました。

{{ config('app.name') }}
@endcomponent
```

これで準備が整いました。
さあここからがキューです。

https://laravel.com/docs/5.7/queues
https://readouble.com/laravel/5.7/ja/queues.html

`config/queue.php`でキューの設定をします。
ですが今回はまだデフォルトのままで、`.env`でキューのドライバーをデータベースに変更します。

```.env
QUEUE_CONNECTION=database
```

次にキュー用のテーブルを作ります。Artisanコマンドが用意されているので便利です。
ひとつはキューのジョブテーブルで、もうひとつは実行が失敗したジョブのテーブルです。

```
$ php artisan queue:table
$ php artisan queue:failed-table
$ php artisan migrate
```

キューで何を処理するかを記述するジョブクラスを作ります。

```
$ php artisan make:job SendRegisteredMail
```

作られたジョブクラスは`app/Jobs`以下に配置されます。
`SendRegisteredMail.php`にはコンストラクタと`handle()`メソッドが用意されています。
コンストラクタでジョブに必要なデータを渡し、`handle()`で実際にやりたい処理を行うという流れです。

```app/Jobs/SendRegisteredMail.php
<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;

use App\User;
use App\Mail\UserRegistered;
use Illuminate\Support\Facades\Mail;

class SendRegisteredMail implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected $user;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct(User $user)
    {
        $this->user = $user;
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        Mail::to($this->user->email)->send(new UserRegistered($this->user));
    }
}
```

そして、ユーザ登録部分にキューを発行する処理を追加します。
ジョブクラスの`dispatch()`を実行します。

```app/Http/Controllers/Auth/RegisterController.php
<?php

namespace App\Http\Controllers\Auth;

use App\User;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\Auth\RegistersUsers;

use App\Jobs\SendRegisteredMail;

class RegisterController extends Controller
{

    〜〜〜〜〜

    /**
     * Create a new user instance after a valid registration.
     *
     * @param  array  $data
     * @return \App\User
     */
    protected function create(array $data)
    {
        $user = User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => Hash::make($data['password']),
        ]);

        SendRegisteredMail::dispatch($user);

        return $user;
    }
}
```

さて、これでユーザ登録するとメール送信のキューが発行されます。
しかしこのままだとキューを取り出して処理がされません。
キューワーカでのキューの監視が必要となってきます。
そこでArtisanコマンドでキューワーカを起動します。

```
$ php artisan queue:work
```

これでキューが監視され処理されます。ユーザ登録するとメールが送信されたことが確認出来ると思います。
キューワーカのコンソールでも以下のように出力されます。
失敗すると設定した回数分リトライされます。

```
$ php artisan queue:work
[2019-01-30 02:00:00][2] Processing: App\Jobs\SendRegisteredMail
[2019-01-30 02:00:00][2] Processed:  App\Jobs\SendRegisteredMail
```

公式ドキュメントでも述べられているとおり、キューワーカを永続的に実行するためにはSupervisorのようなプロセスモニタが必要です。

という感じで、キューという仕組みとしてはわかりやすいけれど実装するとしたら難しい機構をLaravelでは手軽に使用出来ることがお分かりいただけたと思います。
公式ドキュメントによるともっと詳細に設定出来るので調べてみるとよいと思います。それでは！


##### 追記

そもそもLaravelのメール送信機能もキューを使ってるっぽいので冗長になっているのでは……