Laravelでジョブとキューを理解する

#はじめに
Laravelにはジョブとキューと言う機能があります
本記事ではジョブとキューとは何か
そして、ジョブとキューを使って非同期な機能を実装する
サンプルを作成していきます

#キュートとは
キューはある決まった処理を`非同期で実行する`ための仕組み
キューにジョブを登録していきその処理を実行していく

#ジョブとは
ジョブとは`処理を実行する本体`になります
キューにジョブが登録され、キューにあるジョブが実行されます
処理自体を担当するのがジョブになります

#サンプル作成
では実際に、ジョブとキューを使ったサンプルを作っていき
ジョブとキューの仕組みについて理解していきます
今回作るサンプルは、Userを一覧表示し`/{id?}`に対してidの部分にUserのIDを指定します
その指定されたUserの名前に[JOB]と言う文字を付けたり、外したりを非同期で行います
Laravelプロジェクトは各自で作成してください

#キュー用のテーブルを作成する
まずジョブが登録されるキューのテーブルを作成していきます
コマンドにて下記を実行してください

```
$ php artisan queue:table
```

````
$ php artisan migrate
```
これでキュー用のテーブル`jobs`
キューの実行失敗時に使用されるテーブル`failed_jobs`が作成されます
また、今回使用するUsersテーブルも作成されてるかと思います
また、バージョンの違いにより`failed_jobs`テーブルが作成されていない場合は
下記コマンドを実行し作成してくだ際

```
$ php artisan queue:failed-table
```
```
$ php artisan migrate
```
これで必要なテーブルは用意できました

#サンプルデータの作成
Seederファイルを作成しダミーのデータを作成します
下記コマンド実行

```
$ php artisan make:seeder PersonTableSeeder
```
`database/seeds`にファイルが作成されます
作成したファイルに追加していきます

```PersonTableSeeder.php
<?php

use Illuminate\Database\Seeder;

class PersonTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $param = [
            'name' => '太郎',
            'email' => 'taro@gmail.com',
            'password' => '99999999'
        ];

        DB::table('users')->insert($param);

        $param = [
            'name' => '花子',
            'email' => 'hanako@gmail.com',
            'password' => '99999999'
        ];

        DB::table('users')->insert($param);

        $param = [
            'name' => '次郎',
            'email' => 'ziro@gmail.com',
            'password' => '99999999'
        ];

        DB::table('users')->insert($param);
    }
}
```
次に作成したシーダファイルを登録します
`database/seeds/DatabaseSeeder.php`にて下記を記述してください

```
<?php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        $this->call(PersonTableSeeder::class);
    }
}

```
これでシーダファイルと登録が完了しました
下記コマンドを実行します

```
$ php artisan db:seed
```

これでUsersテーブルに3件のダミーデータを用意できました
このデータを一覧表示します

#データの表示
まずはコントローラを作成します

```
$ php artisan make:controller MainController
```


次にルートの定義をします

```web.php
Route::get('/{id?}, 'MainController@index');
```

コントローラーのアクションを実装します

```MainController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\User;
use App\Jobs\SampleJob;

class MainController extends Controller
{
    public function index($id = null) {
        if ($id !== null) {
            $user = User::find($id);
        } else {
            $user = null;
        }

        if ($user != null) {
            SampleJob::dispatch($user)->delay(now()->addMinutes(1));
        }

        $data = User::all();

        return view('main.index', compact('data'));
    }
}
```
`SampleJob::dispatch($user)`とあります
この部分でジョブを呼び出しています
ジョブは`dispatch()`を使い呼び出します
また今回はパスに`/{id?}`としているのでidが入っているかどうかを確認し
もしidがあればそのidに該当するユーザーを所得してジョブの発行の際に引数としてジョブにUserを渡してあげてます
`delay(now()->addMinutes(1))`これで１分後にジョブを実行する処理をしています
ここまでの処理により、idがあるかを判断しidがあれば該当するUserを所得
Userが存在していればジョブを発行し１分後に実行すると言う処理の実装をしました

次に`resources`に`main`ファルだを作成して`index.blade.php`を作成します
`index.blade.php`に下記を追加してください

```index.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>ユーザー表示</title>
</head>
<body>
    <ul>
        @foreach($data as $item)
            <li>{{ $item->name }}</li>
        @endforeach
    </ul>
</body>
</html>
```

これで、登録したダミーデータをリストで一覧表示することができました
それではこのデータに対して、ジョブとキューを利用して加工していきます

#Jobクラスの作成
コマンドにて下記を実行する

```
$ php artisan make:job SampleJob
```
実行すると、`app/Jjobs`フォルダが作成され中に`SampleJob.php`ファイルが作成されています
これがジョブファイルになります
ではジョブファイルに追加していきます

```SampleJob.php
<?php

namespace App\Jobs;

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

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

    protected $user;

    public function __construct(User $user)
    {
        $this->user = $user;
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        $sufix = '[Job]';

        if (strpos($this->user->name, $sufix)) {
            $this->user->name = str_replace($sufix, '', $this->user->name);
        } else {
            $this->user->name .= $sufix;
        }

        $this->user->save();
    }
}
```
ジョブでは実行したい処理を`handle()`メソッドに記入します
そこに記述された処理が実行されています
また、今回の例では`__construct()`でUserを受け取っています
このUserは`MainController`の`index()`アクションでジョブの発行の際に引数で渡してあげたUserになります
受け取ったUserをプロパティで管理しています
そのUserを使用し、もしUserのnameに[JOB]が含まれていればそれを削除し
含まれていなければ、名前に追加してDBに登録する処理を書いています
これがジョブ自体の処理になります
このジョブの処理を実行したければ先ほどの`MainController`の`index()`メソッドで行ったように
`dispatch`を使用してジョブを発行させてあげれば良いのです

ただ、これだけではジョブは使用できません
ジョブはサービスプロバイダーに登録してあげる必要があります

#サービスプロバイダーの作成
ジョブ登録専用のプロバイダーを作成します

```
$ php artisan make:provider JobServiceProvider
```

`app/providers`フォルダに作成したプロバイダーファイルが作られます
`JobServiceProvider.php`を下記のように記述してください

```JobServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class JobServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bindMethod(SampleJob::class.'@handle', function($job, $app) {
            return $job->handle();
        });
    }

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }
}
```
`register()`メソッドでジョブクラスの登録を行っています
第一引数にジョブクラス名.`'@handle'`を渡してあげます
第二引数にクロージャを渡します
クロージャの第一引数にはジョブクラスのインスタンスが渡され、第二引数にはサービスコンテナが渡されます
クロージャ内で`$job`ジョブのインスタンスで`handle()`を呼び出すことでジョブが実行できるようにすることができます

ジョブのサービスプロバイダーへの登録は以上になります

最後に.envの編集をします

#.evnの編集
デフォルトではキューは同期で動くようになっているため
非同期で動かすために.envでキューをデータベースを利用するように変更します

```.env
QUEUE_CONNECTION=database
QUEUE_DIRVER=database
```
これで全ての準備が完了しました
それでは実際に動作を確認してみましょう

#サンプルの実行
ブラウザから`/1`とURLを叩いてみましょう
そのまま待ちます
１分後`/`にアクセスしてみてください
`太郎[JOB]`となっていれば成功です
１分後にジョブを実行するようになっているのでURLで指定したidのユーザーに[JOB]が１分後に付きます
また、ついているユーザーのidを再度叩くことで次は[JOB]が消えることも確認してみてください
また、`/1`とアクセスした際にすぐにデータベースのjobsテーブルを確認してみてください
何かデータが入っているかと思います
また、１分後に[JOB]が付いた後に再度jobsテーブルを確認してみてください
中身がなくなっているかと思います
このように発行されたジョブをキューに登録していくことでDBに用意されそれが実行されDBから消えます

これがジョブとキューになり非同期で実行することができました
長くなりましたので最後に簡単にまとめます

#まとめ
###ジョブとキュートは
- キュートは非同期で処理を実行するもの
- ジョブとは実装する処理そのものである

###ジョブとキューを非同期で利用する手順
- ジョブを作成する
- ジョブを登録するサービスプロバイダを作成しジョブを登録する
- キュー専用のテーブルをartisanコマンドで作成する
- .envを編集する
- ジョブのhandle()メソッドに処理を書く
- ジョブを発行したい際にdispatch()を利用してジョブを使用する