Readouble

Laravel 5.0 コマンドバス

イントロダクションIntroduction

Laravelコマンドバスは、シンプルで分かりやすい「コマンド」として実行する必要のあるアプリケーションのタスクをカプセル化する便利なメソッドです。コマンドの目的を理解するために、ポッドキャストをユーザーに購入してもらうアプリケーションを構築しているとしましょう。The Laravel command bus provides a convenient method of encapsulating tasks your application needs to perform into simple, easy to understand "commands". To help us understand the purpose of commands, let's pretend we are building an application that allows users to purchase podcasts.

ユーザーがポッドキャストを購入すると、色々な処理を実行する必要が起きます。例えば、ユーザーのクレジットカードに課金したり、領収を表すデータベースへレコードを追加したり、購入の確認メールを送ったりです。多分、そのユーザーがポッドキャストを購入できるのか、様々なバリデーションを行う必要もあるでしょう。When a user purchases a podcast, there are a variety of things that need to happen. For example, we may need to charge the user's credit card, add a record to our database that represents the purchase, and send a confirmation e-mail of the purchase. Perhaps we also need to perform some kind of validation as to whether the user is allowed to purchase podcasts.

コントローラーメソッドへ、こうしたロジックを全部突っ込むこともできます。しかし、多くの点で良くありません。多分、コントローラは要求がされるだろう別のHTTPアクションもたくさん処理し、各コントローラーメソッドには複雑なロジックがつめ込まれ、膨れ上がり、結果として読みづらくなってしまうのが第一の欠点です。第二に、コントローラー外でポッドキャスト購入のロジックを再利用するのが難しいことです。第三に、ポッドキャスト購入ロジックをテストするため、HTTPリクエストのスタブを生成しなくてはなりませんし、アプリケーションへ送る完全なリクエストを作成しなくてはならないため、コントローラーのユニットテストが困難になることです。We could put all of this logic inside a controller method; however, this has several disadvantages. The first disadvantage is that our controller probably handles several other incoming HTTP actions, and including complicated logic in each controller method will soon bloat our controller and make it harder to read. Secondly, it is difficult to re-use the purchase podcast logic outside of the controller context. Thirdly, it is more difficult to unit-test the command as we must also generate a stub HTTP request and make a full request to the application to test the purchase podcast logic.

ロジックをコントローラーに詰め込む代わりに、例えばPurchasePodcastコマンドのように、「コマンド」オブジェクトとしてカプセル化することも選択できます。Instead of putting this logic in the controller, we may choose to encapsulate it within a "command" object, such as a PurchasePodcast command.

コマンド作成Creating Commands

新しいコマンドクラスは、make:command Artisan CLIで生成できます。The Artisan CLI can generate new command classes using the make:command command:

php artisan make:command PurchasePodcast

生成されたクラスは、app/Commandsディレクトリーへ設置されます。生成されたコマンドには、デフォルトで2つのメソッドがあります。コンストラクターとhandleメソッドです。もちろんコンストラクターはコマンドへ適切なオブジェクトを渡すために使えます。一方のhandleメソッドは、コマンドを実行します。例をご覧ください。The newly generated class will be placed in the app/Commands directory. By default, the command contains two methods: the constructor and the handle method. Of course, the constructor allows you to pass any relevant objects to the command, while the handle method executes the command. For example:

class PurchasePodcast extends Command implements SelfHandling {

	protected $user, $podcast;

	/**
	 * 新しいコマンドインスタンス生成
	 *
	 * @return void
	 */
	public function __construct(User $user, Podcast $podcast)
	{
		$this->user = $user;
		$this->podcast = $podcast;
	}

	/**
	 * コマンド実行
	 *
	 * @return void
	 */
	public function handle()
	{
		// ポッドキャスト購入ロジックを処理する…

		event(new PodcastWasPurchased($this->user, $this->podcast));
	}

}

handleメソッドでも依存をタイプヒントで指定でき、サービスコンテナにより自動的に注入されます。例えば:The handle method may also type-hint dependencies, and they will be automatically injected by the service container[/docs/{{version}}/container]. For example:

	/**
	 * コマンド実行
	 *
	 * @return void
	 */
	public function handle(BillingGateway $billing)
	{
		// ポッドキャスト購入ロジックを処理する…
	}

コマンドデスパッチDispatching Commands

コマンドはできました。ではどうやってデスパッチするのでしょう?もちろん、handleメソッドを直接呼び出すこともできます。しかし、コマンドをLaravel「コマンドバス」を通して実行する方法には、後ほど説明する多くの利点があります。So, once we have created a command, how do we dispatch it? Of course, we could call the handle method directly; however, dispatching the command through the Laravel "command bus" has several advantages which we will discuss later.

アプリケーションのベースコントローラーを覗いてみれば、DispatchesCommandsトレイトが見つかります。このトレイトは、コントローラーからdispatchメソッドを呼び出せるようにします。以下のようにです。If you glance at your application's base controller, you will see the DispatchesCommands trait. This trait allows us to call the dispatch method from any of our controllers. For example:

public function purchasePodcast($podcastId)
{
	$this->dispatch(
		new PurchasePodcast(Auth::user(), Podcast::findOrFail($podcastId))
	);
}

コマンドバスはコマンド実行の面倒を見ます。そして、呼びだされたIoCコンテナがhandleメソッドへ必要な依存を注入します。The command bus will take care of executing the command and calling the IoC container to inject any needed dependencies into the handle method.

お望みであれば、どんなクラスへでもIlluminate\Foundation\Bus\DispatchesCommandsトレイトを付け加えることができます。クラスのコンストラクターを通して、コマンドバスのインスタンスを受け取りたいのであれば、Illuminate\Contracts\Bus\Dispatcherインターフェイスをタイプヒントに指定してください。最後に、Busファサードを使い、簡単にコマンドをデスパッチする方法を紹介します。You may add the Illuminate\Foundation\Bus\DispatchesCommands trait to any class you wish. If you would like to receive a command bus instance through the constructor of any of your classes, you may type-hint the Illuminate\Contracts\Bus\Dispatcher interface. Finally, you may also use the Bus facade to quickly dispatch commands:

	Bus::dispatch(
		new PurchasePodcast(Auth::user(), Podcast::findOrFail($podcastId))
	);

リクエストからコマンドのプロパティーをマップするMapping Command Properties From Requests

HTTPリクエストの変数をコマンドへマップしたいと考えるのは当然でしょう。それぞれのリクエストを手動で無理やりマップする代わりに、Laravelでは簡単に実現できるヘルパメソッドを用意しています。DispatchesCommandsトレイトで使用できる、dispatchFromメソッドを取り上げてみてみましょう。It is very common to map HTTP request variables into commands. So, instead of forcing you to do this manually for each request, Laravel provides some helper methods to make it a cinch. Let's take a look at the dispatchFrom method available on the DispatchesCommands trait:

$this->dispatchFrom('Command\Class\Name', $request);

このメソッドは指定されたコマンドクラスのコンストラクターを調べ、それからHTTPリクエスト(もしくは他のArrayAccessオブジェクト)から変数を取り出し、必要なコマンドのコンストラクター引数を埋めます。ですから、もしコマンドクラスがコンストラクターで$firstName変数を取る場合、コマンドバスはHTTPリクエストからfirstNameパラメーターを取り出そうとします。This method will examine the constructor of the command class it is given, and then extract variables from the HTTP request (or any other ArrayAccess object) to fill the needed constructor parameters of the command. So, if our command class accepts a firstName variable in its constructor, the command bus will attempt to pull the firstName parameter from the HTTP request.

dispatchFromメソッドはさらに第3引数に配列を指定できます。この配列はリクエストからは埋められないコンストラクター引数を埋めるために使用されます。You may also pass an array as the third argument to the dispatchFrom method. This array will be used to fill any constructor parameters that are not available on the request:

$this->dispatchFrom('Command\Class\Name', $request, [
	'firstName' => 'Taylor',
]);

コマンドのキュー投入Queued Commands

コマンドバスは現在のリクエストサイクル中で、同期的にジョブを実行するだけではなく、Laravelのキュージョブとして非同期に実行する重要な手法も提供しています。では同期的に実行する代わりに、どうやってコマンドバスへ、バックグラウンド処理を行うためにジョブをキューに入れろと指示するのでしょうか。簡単です。最初に新しいコマンドを生成するときに、--queuedフラグをコマンドへ付けるだけです。The command bus is not just for synchronous jobs that run during the current request cycle, but also serves as the primary way to build queued jobs in Laravel. So, how do we instruct command bus to queue our job for background processing instead of running it synchronously? It's easy. Firstly, when generating a new command, just add the --queued flag to the command:

php artisan make:command PurchasePodcast --queued

すぐ気がつくでしょうが、これによりいくつかの機能がコマンドに追加されます。すなわちIlluminate\Contracts\Queue\ShouldBeQueuedインターフェイスとSerializesModelsトレイトです。これらは、コマンドバスにコマンドをキューに投入するように指示し、同時にコマンドのプロパティーとしてストアされたEloquentモデルを優雅にシリアライズ、非シリアライズします。As you will see, this adds a few more features to the command, namely the Illuminate\Contracts\Queue\ShouldBeQueued interface and the SerializesModels trait. These instruct the command bus to queue the command, as well as gracefully serialize and deserialize any Eloquent models your command stores as properties.

もし、既に存在するコマンドをキュー実行のコマンドに変更したければ、単にIlluminate\Contracts\Queue\ShouldBeQueuedインターフェイスを手動でクラスへ実装してください。これはメソッドを含んでおらず、主にデスパッチャーがコマンドへ投入する「目印のインターフェイス」として使用しています。If you would like to convert an existing command into a queued command, simply implement the Illuminate\Contracts\Queue\ShouldBeQueued interface on the class manually. It contains no methods, and merely serves as a "marker interface" for the dispatcher.

それから、コマンドを通常通り書いてください。それがバスへデスパッチされると、バックグラウンドで処理するため自動的にキューへ投入します。Then, just write your command normally. When you dispatch it to the bus that bus will automatically queue the command for background processing. It doesn't get any easier than that.

キューされるコマンドのインターフェイスの詳細は、キューのドキュメントを参照してください。For more information on interacting with queued commands, view the full queue documentation[/docs/{{version}}/queues].

コマンドパイプラインCommand Pipeline

ハンドラにディスパッチされる前に、「パイプライン」中の他のクラスへ、コマンドを通すことができます。コマンドパイプラインは、実行するのがコマンドという違いだけで、まるでHTTPミドルウェアのように動作します!例えば、コマンド全体の操作をデータベーストランザクションでラップしたり、実行をログしたりできます。Before a command is dispatched to a handler, you may pass it through other classes in a "pipeline". Command pipes work just like HTTP middleware, except for your commands! For example, a command pipe could wrap the entire command operation within a database transaction, or simply log its execution.

パイプをバスに追加するには、App\Providers\BusServiceProvider::bootメソッドから、ディスパッチャーのpipeThroughメソッドを呼び出します。To add a pipe to your bus, call the pipeThrough method of the dispatcher from your App\Providers\BusServiceProvider::boot method:

$dispatcher->pipeThrough(['UseDatabaseTransactions', 'LogCommand']);

コマンドのパイプは、ミドルウェアと同様に、handleメソッドで定義します。A command pipe is defined with a handle method, just like a middleware:

class UseDatabaseTransactions {

	public function handle($command, $next)
	{
		return DB::transaction(function() use ($command, $next)
		{
			return $next($command);
		});
	}

}

コマンドパイプのクラスは、IoCコンテナを通じて依存解決されるため、コンストラクターでタイプヒントによる依存を指定することもできます。Command pipe classes are resolved through the IoC container[/docs/{{version}}/container], so feel free to type-hint any dependencies you need within their constructors.

コマンドパイプを「クロージャー」で定義することもできます。You may even define a Closure as a command pipe:

$dispatcher->pipeThrough([function($command, $next)
{
	return DB::transaction(function() use ($command, $next)
	{
		return $next($command);
	});
}]);

章選択

Artisan CLI

設定

明暗テーマ
light_mode
dark_mode
brightness_auto システム設定に合わせる
テーマ選択
photo_size_select_actual デフォルト
photo_size_select_actual モノクローム(白黒)
photo_size_select_actual Solarized風
photo_size_select_actual GitHub風(青ベース)
photo_size_select_actual Viva(黄緑ベース)
photo_size_select_actual Happy(紫ベース)
photo_size_select_actual Mint(緑ベース)
コードハイライトテーマ選択

明暗テーマごとに、コードハイライトのテーマを指定できます。

テーマ配色確認
スクリーン表示幅
640px
80%
90%
100%

768px以上の幅があるときのドキュメント部分表示幅です。

インデント
無し
1rem
2rem
3rem
原文確認
原文を全行表示
原文を一行ずつ表示
使用しない

※ 段落末のEボタンへカーソルオンで原文をPopupします。

Diff表示形式
色分けのみで区別
行頭の±で区別
削除線と追記で区別

※ [tl!…]形式の挿入削除行の表示形式です。

テストコード表示
両コード表示
Pestのみ表示
PHPUnitのみ表示
和文変換

対象文字列と置換文字列を半角スペースで区切ってください。(最大5組各10文字まで)

本文フォント

総称名以外はCSSと同様に、"〜"でエスケープしてください。

コードフォント

総称名以外はCSSと同様に、"〜"でエスケープしてください。

保存内容リセット

localStrageに保存してある設定項目をすべて削除し、デフォルト状態へ戻します。

ヘッダー項目移動

キーボード操作