※前回の記事は以下です
概要
- 今回は 構成の概念 編
リクエストのライフサイクル
概論
- リクエストの受け口は
public/index.php
のみ- Composerが生成したオートローダーの定義をロードする
-
bootstrap/app.php
からアプリケーション/サービスコンテナのインスタンスを取得する
HTTP/Consoleカーネル
-
app/Http/Kernel.php
- 送信されたリクエストを渡され、処理する。全リクエストフローの中心に位置し動作する
-
Illuminate\Foundation\Http\Kernel
クラスを継承し拡張している - エラー処理、ログ設定、アプリケーション動作環境の決定、そのほか実際にリクエストが処理される前に行う必要のあるタスクなどのbootstrappers(起動コード)の配列を定義する
- HTTPセッションの読み書き、アプリケーションがメンテナンスモードであるかの決定、CSRFトークンの確認などのHTTPミドルウェアのリストも定義する
- サービスプロバイダ
-
config/app.php
ファイルのproviders
配列で設定されている- 親クラス
Illuminate\Foundation\Http\Kernel
に定義されている$bootstrappers
の中に角サービスプロバイダが登録されている
- 親クラス
- サービスプロバイダはデータベース、キュー、バリデーション、ルーティングなど、フレームワークの様々なコンポーネントの初期起動処理に責任をもつ
- カーネルの handle メソッド内で実行されている
- プロバイダーを自作する場合は
app/Providers
に作成すること
-
- リクエストのディスパッチ
- ルーターはそのリクエストをルートかコントローラにディスパッチし、その時にルートに指定されているミドルウェアも実行する
- これも、カーネルの handle メソッド内で実行されている
サービスコンテナ
- DI(依存性注入)の実現方法を説明している?
- それぞれのサービスプロバイダの中で、必要なサービス(=注入されるクラス?)をコンテナに登録している
- 以下の記事がすごく参考になりました
結合
-
シンプルな結合
- コンテナ自身がリゾルバ―の引数として渡される(=第二引数に渡すクラス名を、第一引数に文字列として指定する)
-
意味としては、
app()->bind('呼び出しキーワード', 'インスタンス化方法');
$this->app->bind('HelpSpot\API', function ($app) { return new HelpSpot\API($app->make('HttpClient')); });
-
他の結合方法との違いが説明できない。。。
- make() が呼ばれる度に都度インスタンス生成する(=シングルトンでない)
-
シングルトン結合
-
一度シングルトン結合が解決されるとそれ以降、この結合が参照されるたび、コンテナは同じオブジェクトインスタンスを返す
$this->app->singleton('hogehoge', function ($app) { return new HelpSpot\API($app->make('HttpClient')); });
-
-
インスタンス結合
- 既に存在するオブジェクトのインスタンスをコンテナに結合する
-
以降の呼び出しには、登録したインスタンスが返される
$api = new HelpSpot\API(new HttpClient); $this->app->instance('foobar', $api);
-
プリミティブ結合
-
プリミティブな値をコンテナに登録できる
$this->app->when('App\Http\Controllers\UserController') ->needs('$variableName') ->give($value);
-
具体的な呼び出し方は?
- コントローラー側でのコンストラクタで型を指定すれば、依存解決機能を使って自動的にインスタンス化して渡してくれる
- もしくは、
app()->make('hogehoge');
でインスタンス化する
-
インターフェイスと実装の結合
- 呼び出しキーワードにインターフェースを指定して具象クラスをインスタンス化したい場合、省略して書けるよ
コンテキストによる結合
-
when()->needs()->give()
を使えば、同じキーワードでも呼び出し元に沿ったインスタンスを返すよ
タグ付け
-
tag()
を使えば、タグ付けした一連のサービスをtagged()
で取得できるよ
結合の拡張
-
extend()
でサービスの解決結果を修正できるよ
依存解決
-
makeメソッド
-
$this->app->make('HelpSpot\API');
で、コンテナに結合されたサービスをインスタンス化するよ - $app変数へアクセスできない場所なら、
resolve('HelpSpot\API');
でいけるよ -
makeWith()
は具体的にどこで使うの?
-
-
自動注入
- 以下のコンストラクタでは、自動的に型解決してインスタンスが注入されるよ
- コントローラ
- イベントリスナ
- キュージョブ
- ミドルウェア
- コントローラーのコンストラクタで使用するリポジトリを受け取る、なんかはこの仕組みで受け取ってるよ
- 以下のコンストラクタでは、自動的に型解決してインスタンスが注入されるよ
コンテナイベント
- コンテナが解決された 後 に実行する関数を定義できる
- コールバック関数と考えてよさそう
- すべてのコンテナが解決された時に実行する
$this->app->resolving(function ($object, $app) {});
- 特定のコンテナが解決された時に実行する
$this->app->resolving(HelpSpot\API::class, function ($api, $app) {});
PSR-11
- PSR ってなに?
- PSR-11 で定義された依存性注入コンテナの共通インタフェースを使用しているよ
- だから、use でインターフェースを指定すれば、どこでもサービスコンテナを呼び出せるよ
- でも、なんでもかんでもコレを使うのは良くないよ。基本的には依存解決を使って指定してね。
- 以下が詳しいよ
サービスプロバイダ
- 初期起動処理を行う心臓部
- サービスコンテナの結合や、イベントリスナ、フィルター、それにルートなどを登録することを指す
-
config/app.php
にデフォルトのサービスプロバイダが定義されているよ- いっぱいあるけど、ほとんどは遅延ロードだよ
- 実際に手を動かした内容は このコミット を参照
サービスプロバイダの記述
-
Illuminate\Support\ServiceProvider
を継承しましょうね - register と boot の 2 つのメソッドを持つ
- register ではサービスコンテナの登録 のみ を行うこと
- なんで?
- サービスプロバイダがまだロードしていないサービスを意図せず使ってしまうこともありえるから
- ルールとして「そうしましょうね」ということ。守らないと意図しないエラーが起こる可能性がありますよ
- なんで?
- register ではサービスコンテナの登録 のみ を行うこと
-
以下コマンドでプロバイダを新規作成できるよ
$ php artisan make:provider RiakServiceProvider
-
Registerメソッド
- 一つずつ $app から登録してもいいけど、
$bindings
と$singletons
プロパティを宣言すれば、それぞれのプロパティの中身を自動で登録してくれるよ
- 一つずつ $app から登録してもいいけど、
-
Bootメソッド
- 他の全サービスプロバイダが登録し終えてから呼び出されるよ
- ビューコンポーサの登録やサービスコンテナ使って処理したい場合はここに記述するよ
- 依存解決もしてくれるから、引数を追加すれば自動でインスタンスを受け取れるよ
プロバイダの登録
- サービスプロバイダを新規作成したら
config/app.php
の$providers
プロパティに追加しましょうね
遅延プロバイダ
- サービスコンテナの登録 だけ を目的とするなら、遅延ロードさせた方がパフォーマンスは良いよね
- サービスプロバイダにて、以下を行うと指定した結合は遅延ロードさせれるよ
-
protected $defer = true;
を宣言 -
provides()
メソッドを定義して、結合名の配列を返却する
-
ファサード
- この方の記事にはいつも助けられてます。ありがとうございます。
- 要は、サービスコンテナに登録したクラスのメソッドを static メソッドとしてアクセスできるようにする仕組み
- PHP の マジックメソッド で実現している
- 実際に手を動かした内容は このコミット を参照
いつファサードを使うか
- ファサードを定義しまくって一つのコントローラで呼び出しまくると、そのコントローラの責任が大きくなるから良くないよ、という話
- ファサード 対 依存注入
- 依存注入は、実行環境などの諸条件でモックを使用できるから便利だよね
- でも、ファサードで呼び出すメソッドは「実行環境などの諸条件でモックを使用」したものに対しても実行できるから、ファサードも負けて無いよ、ということかな。
- ファサード 対 ヘルパ関数
- ファサードとヘルパ関数は同じ使い方ができるよ、という話
ファサードの仕組み
- 冒頭にも書いたけど、
__callStatic()
マジックメソッドにより実現してますよ - 以下コマンドでコントローラを作って、ドキュメント通りに実装してみた
$ php artisan make:controller UserController
- サービスコンテナに登録したクラスをファサードとして使用するには、
-
Illuminate\Support\Facades\
にクラスを作る - [1.] のクラスには
getFacadeAccessor()
メソッドのみ定義し、サービスコンテナに登録された結合名を返す -
config/app.php
のaliases
に [1.] を登録する
-
リアルタイムファサード
- 以下コマンドでモデルを作って、ドキュメント通りに実装してみた
$ php artisan make:model Podcast
- [ファサードの仕組み]でやったような手順を踏まなくても、特定のクラスをファサードクラスとして使用できるよ、という話
- テスト方法も書いてあるが、少しめんどくさそう。モックを注入する方がわかり良いのではないか。