Laravel に限らず Web アプリケーションフレームワークは基本的に
ルーティング→コントローラー→(なにか処理)→ビューという流れ。
この(なにか処理)をどこに書くかというのが問題になる。
- コントローラーに書くのはファットコントローラーになるからやめろと言われる。
- MVCの時代にはモデルに書くとされてたけどほぼモデル=DBなWebMVCではなんとなく違う。Eloquent にメソッド増やしたくない。
- 少し前はサービスクラスを作れと言われてたけどサービスクラスって何?な曖昧な状態では上手く作れない。コントローラーに書いてたことを分離しただけみたいなことは自分もまだやってしまう。
俺が悪かった。素直に間違いを認めるから、もうサービスクラスとか作るのは止めてくれ
Laravel 5.5 では
キューで使うJobクラスをサービスクラスの代わりにするといいと気付いた。かなり前に気付いてたけどやりたいことはできないと思ってた。
https://readouble.com/laravel/5.5/ja/queues.html
「サービス」よりは「ジョブ」のほうが一つの処理の塊としては理解しやすい。
なんならジョブクラスから更にサービスクラスを呼んでもいい。
ジョブならキューで非同期な実行と dispatch_now()
で即実行を使い分けられる。
実は5.4でも
sync
ドライバーなら返り値を得られたらしい。もしくはimplements ShouldQueue
を削除するか。
$bar = dispatch(new FooJob());
$job = (new FooJob())->onConnection('sync');
$bar = dispatch($job);
前に試した時はジョブ内でreturn response()
とかやろうとしてたから間違ってた。
5.4以前のことはもう考慮しないので忘れる。
実際に作ってみる
Job
php artisan make:job FooJob
サンプルだとこういう↑シンプルな例が多いけど My/FooJob
などでディレクトリを分けられるから規模が大きくなったら分ける。コントローラーやコマンドでも同じ。
php artisan make:job My/FooJob
handle()
内になにかの処理を書く。
/**
* @var string
*/
protected $foo;
/**
* Create a new job instance.
*
* @param string|null $foo
*/
public function __construct(string $foo = null)
{
$this->foo = $foo;
}
/**
* Execute the job.
*
* @return string
*/
public function handle(): string
{
return 'Bar';
}
Controller
php artisan make:controller FooController
数行だけの小さいコントローラーにできる。
use App\Jobs\FooJob;
class FooController extends Controller
{
public function __invoke(Request $request)
{
$foo = $request->input('foo');
$bar = dispatch_now(new FooJob($foo));
return view('foo')->with(compact('bar'));
}
}
これはコントローラーの仕事を完璧に遂行してる…。
artisanコマンド
php artisan make:command FooCommand
同じジョブをartisanコマンドからも使う。キューで実行。
public function handle()
{
FooJob::dispatch('foo');
}
こっちも dispatch_now()
で使ってもいい。
一番使いそうなコントローラーとartisanコマンドを例にしてるけどアプリ内ならどこからでも使える。
テスト
コントローラーのテストは Bus::fake()
でモックできるのでジョブクラスは無視できる。なるべく new
は使いたくないけどジョブクラスは使っても大丈夫そう。
https://readouble.com/laravel/5.5/ja/mocking.html
ジョブのテストは handle()
を実行すればいいだけなので単体で可能。
$job = new FooJob($foo);
$bar = $job->handle();
おわり
キューを前提にしてるけどそもそもキューはどのくらい使われてるんだろう…。dispatch_now()
だけ使うならキューなしというか sync
ドライバーでも大丈夫なはず。