メールはキューを使って送信しよう!（Laravel/Lumen使ってたらね）


「サンクスメールを送信するとかの処理は即時じゃなくてもいいから、サイトのレスポンスはとにかく速く」という雰囲気になった時はLaravel/Lumenのキューを使う機会が来たということじゃないでしょうか。
というわけで、ちょっとやってみました。


## 前提

* サンプルコードはLumen5.1です（Laravel5.1~5.3でも確認済です）
* キュードライバは、データベースドライバを使います。
* 設定はこの記事の下の方にある「補足」の通りです。


## Laravel/Lumenのキューに対する自分の中の理解


ジョブクラスのインスタンスをjobsテーブルに登録すると、Laravel/Lumenがジョブをキューとして実行していってくれる。


## やる事

1. jobsテーブルを作成する
* ジョブクラスを作成する
* キューリスナーを実行する


### 1.jobsテーブルを作成する

これは[ドキュメント](https://lumen.laravel.com/docs/5.1/queues#introduction)にある通り、マイグレーションファイルを生成して、マイグレーションを実行します。

```artisanコマンドを実行
php artisan queue:table
php artisan migrate
```


### 2.ジョブクラスを作成する

これも[ドキュメント](https://lumen.laravel.com/docs/5.1/queues#writing-job-classes)にある通りですが、以下のようにプロパティを使う場合は必ず指定しとかないと、キューの実行時に例外を投げよります。


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

namespace App\Jobs;

use Illuminate\Contracts\Bus\SelfHandling;
use Illuminate\Contracts\Mail\Mailer;
use Illuminate\Contracts\Queue\ShouldQueue;

class SendEmail extends Job implements SelfHandling, ShouldQueue {

    /** @var string view name */
    protected $view;
    /** @var array ビューに割り当てるデータ */
    protected $data;
    /** @var array メールの送信先等の属性 */
    protected $mail;

    public function __construct($view, $data, $mail)
    {
        $this->view = $view;
        $this->data = $data;
        $this->mail = $mail;
    }

    /**
     * @param Mailer $mailer
     */
    public function handle(Mailer $mailer)
    {
        $mail = $this->mail;
        $mailer->send($this->view, $this->data, function ($message) use ($mail)
        {
            $message->subject($mail['subject'])
                    ->from($mail['from'], $mail['f_name'])
                    ->to($mail['to'], $mail['to_name']);
        });
    }

}
```

Laravel5.3ではクラスの継承などが以下のように変更されています。

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

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Mail\Mailer;
use Illuminate\Contracts\Queue\ShouldQueue;

class SendEmail implements ShouldQueue {

    use InteractsWithQueue, Queueable, SerializesModels;

    /** @var string view name */
    protected $view;
    /** @var array ビューに割り当てるデータ */
    protected $data;
    /** @var array メールの送信先等の属性 */
    protected $mail;

    public function __construct($view, $data, $mail)
    {
        $this->view = $view;
        $this->data = $data;
        $this->mail = $mail;
    }

    /**
     * @param Mailer $mailer
     */
    public function handle(Mailer $mailer)
    {
        $mail = $this->mail;
        $mailer->send($this->view, $this->data, function ($message) use ($mail)
        {
            $message->subject($mail['subject'])
                    ->from($mail['from'], $mail['f_name'])
                    ->to($mail['to'], $mail['to_name']);
        });
    }

}
```

### 3.キューリスナーを実行する

これもまた[ドキュメント](https://lumen.laravel.com/docs/5.1/queues#running-the-queue-listener)にある通りです。

```artisanコマンドを実行
php artisan queue:listen
```

## ジョブのインスタンスをjobsテーブルに登録する

今回は、ブラウザ経由でジョブを登録するので、ラウティングとコントローラーを作成します。
メールのテンプレートにビューファイルを使用するのでついでに作成します。
（laravel5.3ではラウティングファイルに`routes/web.php`を使います）

```app/Http/routes.php
<?php
// for Lumen 5.1
$app->get('reserve', 'ReserveController@store');
// for Laravel 5.1(LTS) ~ 5.3
Route::get('reserve', 'ReserveController@store');
```

```app/Http/Controllers/ReserveController.php
<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Jobs\SendEmail;

class ReserveController extends Controller {

    public function store()
    {
        $mail = [
            'from'    => 'nielsen@test.com',
            'f_name'  => 'Nelsen',
            'to'      => 'poulsen@test.com',
            'to_name' => 'Poulsen',
            'subject' => 'Thanks for your reservation.'
        ];
        // ジョブのインスタンスを生成
        $job = new SendEmail(
            'emails.reservation',
            ['name' => ucfirst('niikunihiro')],
            $mail
            );
        // jobs テーブルに登録
        $this->dispatch($job);
        echo 'finished!';
    }

}
```

```resources/views/emails/reservation.blade.php
Hi! {{$name}}.

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab dolor eius eos hic magnam minus, nihil odio omnis quae quas velit, veritatis. Assumenda earum porro, quisquam repellendus sed tempora voluptatibus.
```


ブラウザで確認するので、ビルトインサーバを立ち上げます。

```artisanコマンド
php artisan serve
```

「[http://localhost:8000/reserve](http://localhost:8000/reserve)」にアクセスすると、ログファイルにメールの内容が書込まれているので確認して終わりです。

```storage/logs/lumen.log
[2015-07-11 07:14:46] lumen.DEBUG: Message-ID: <84c77b0b510235de0e6a54621294c3ef@swift.generated>
Date: Sat, 11 Jul 2015 07:14:46 +0000
Subject: Thanks for your reservation.
From: Nelsen <nielsen@test.com>
To: Poulsen <poulsen@test.com>
MIME-Version: 1.0
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: quoted-printable

Hi! Niikunihiro.

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab dolor eius eos hic magnam minus, nihil odio omnis quae quas velit, veritatis. Assumenda earum porro, quisquam repellendus sed tempora voluptatibus.  
```


## 補足

設定ファイル

```.env
# APP_KEY に 32 文字の乱数（じゃなくてもいいけど）を設定する
# APP_KEY が正しく設定されてないとエラーになります
APP_KEY=Dwwf1OGD9vriSvpWIZhDubkPKWdnNgSi

# データベースドライバを使用
QUEUE_DRIVER=database

# 送信内容をログで見たいので
MAIL_DRIVER=log
```

Lumenの場合は、`illuminate/mail`パッケージを追加する

```composer.json
{
    "require": {
        "laravel/lumen-framework": "5.1.*",
        "vlucas/phpdotenv": "~1.0",
        "illuminate/mail": "5.1.*"
    },
}
```

```composerコマンドを実行
composer update illuminate/mail
```