====== Apricot DIコンテナー ====== --- //[[http://www.y2sunlight.com|y2sunlight]] 2020-05-25// [[apricot:top|Apricot に戻る]] 関連記事 * [[apricot:configuration|Apricot プロジェクトの作成]] * [[apricot:public|Apricot 公開フォルダ]] * [[apricot:core:top|Apricot コア]] * [[apricot:app:top|Apricot アプリ]] * Apricot 拡張 * [[apricot:ext:middleware|Apricot ミドルウェア]] * [[apricot:ext:access-log|Apricot アクセスログ]] * [[apricot:ext:csrf|Apricot CSRF対策]] * [[apricot:ext:user-auth|Apricot ユーザ認証]] * [[apricot:ext:basic-auth|Apricot 基本認証]] * [[apricot:ext:session-auth|Apricot セッション認証]] * [[apricot:ext:interceptor|Apricot インターセプター]] * Apricot DIコンテナー ApricotではDIコンテナーに[[basic-library:league-container:3.3|League/Container]]を採用します。主な用途はコントローラに対する ''Auto Wiring'' です。''Auto Wiring'' とはコンストラクター引数の型ヒントを調べることにより、オブジェクトとそのすべての依存関係を再帰的に自動的に解決する機能の事です。地味な機能ですが、保守性向上の為、コンストラクター・インジェクションは必須と考えて実装することにしました。 また、DIコンテナーを使って、サービスプロバイターの仕組みも実装します。この機能については現版のApricotでは使用する場面が無かったので、スタブコントローラの中に例題をつくりました。 尚、DIコンテナーに関する設定ファイル( ''config/setting/container.setting.php'' )や初期設定ファイル( ''config/setup/container.setup.php'' )は今のところありません。カスタマイズの際は必要に応じて追加して下さい。 ---- ===== コントローラの Auto Wiring ===== 以下は、現状のユーザコントローラのコンストラクタです。 public function __construct() { // モデル $this->user = new User(); ... } このコードを ''Auto Wiring'' によって、次のようにすることがここでの目的です。 public function __construct(User $user) { // モデル $this->user = $user; ... } ''Auto Wiring'' は、コンストラクタの型ヒントによって生成するオブジェクトを判断します。(オブジェクト型でない引数には対応していません) \\ ==== ActionInvokerクラス ==== コントローラを生成( new )しているのは、ActionInvoker クラスの invoke() メソッドです。このメソッドを以下のように変更します。 {{fa>folder-open-o}} ** /apricot/core/Foundation ** delegate(new \League\Container\ReflectionContainer); // Get controller instance $instance = $container->get("\\App\\Controllers\\{$this->controller}"); return call_user_func_array(array($instance, 'invokeAction'), [$this->action, $this->params]); } } * コンテナデリゲートとして ReflectionContainer を登録し ''Auto Wiring'' を有効にします。 * コントローラの生成を 単純なnew演算子から Container の get()メソッドに変更します。 Containerの使い方については、[[basic-library:league-container:3.3|League/Container]]を参照して下さい。 \\ ==== ユーザコントローラ ==== ユーザコントローラ (UserController )のコンストラクタを[[#コントローラの Auto Wiring|上]]で述べたように以下のように変更して下さい。 {{fa>folder-open-o}} ** /apricot/app/Controllers ** user = $user; // インターセプター登録 $this->intercept('insert', 'UserInterceptor@insert'); $this->intercept('update', 'UserInterceptor@update'); // トランザクションアクション登録 $this->transactional('insert','update','delete'); } ... } 修正が終わったら、Apricotのユーザ一覧画面や編集画面を操作して動作確認をして見て下さい。 \\ ===== サービスプロバイター ===== サービスコンテナを使用することで、サービスとサービス間の依存関係を登録しておいて後で取得することができます。例えば、サービスAがモデルBとモデルCを使用しているような場合、サービスコンテナにサービスAを要求すると、自動的にモデルBとCを生成し、それらをサービスAのコンストラクタに与えてサービスAを生成してくれます。 サービスプロバイターは、アプリケーション内の全てのサービスコンテナを登録し整理する方法を提供してくれます。また、サービスプロバイダーではサービスが取得された時点で遅延登録されるため、アプリケーションのパフォーマンス向上にも寄与します。 League/Container でサービスプロバイダーを構築するには以下のステップに従います。 - League/Container の 基本サービスプロバイダークラス( ''AbstractServiceProvider'' )を拡張して 独自のサービスプロバイダーを作ります。 - League/Container の ''Container'' クラスに 独自のサービスプロバイダーを登録します。 Apricotでは、独自のサービスプロバイダーとして ''App\Provider'' クラスを定義し、それを登録する ''App\Foundation\Container'' クラスをシングルトンとして実装します。サービスの使用者は、''App\Foundation\Container''が持っている [[https://www.php-fig.org/psr/psr-11/|PSR-11]] に準じた ''get()'' と ''has()'' を使ってサービスを利用することができます。 \\ ==== Providerクラス ==== 以下に、League/Container の 基本サービスプロバイダークラス( AbstractServiceProvider )を拡張したApricot独自のサービスプロバイダークラス( Provider )を以下に示します。 {{fa>folder-open-o}} ** /apricot/app ** getContainer()->add('user', \App\Models\User::class ); } } このクラスは、名前空間Appの直下に存在し、アプリケーションのモデル及びサービスのマップを提供します。現版のApricotでは、モデルはユーザモデル( User )だけで、サービスについては存在しません。モデルやサービスを追加する場合は、上例に習って適宜追加して下さい。 現版のApricotでは、サービスは存在しませんが、サービス用として以下のフォルダが予約されています。 /apricot/app/Services 尚、League/Container のサービスプロバイダーについての詳細は[[https://container.thephpleague.com/3.x/service-providers/|こちら]]をご覧ください。 \\ ==== App\Foundation\Containerクラス ==== App\Foundation\Containerクラスは、\League\Container\Container クラスを生成し、Apricotのサービスプロバイダー(Provider)を登録したクラスで、シングルトンとして動作します。 使用法: ** Container::{メソッド} ** ^メソッド^機能^ |mixed get(string $id)|識別子idでコンテナのエントリを検索して返します。| |bool has(string $id)|コンテナが指定された識別子idのエントリを返すことができる場合はtrueを返します。| {{fa>folder-open-o}} ** /apricot/app/Foundation ** addServiceProvider(new Provider()); return $container; } } \\ ==== サービスコンテナの使用例 ==== === スタブコントローラ === サービスコンテナをテストするために、スタブコントローラを以下のように修正します。 {{fa>folder-open-o}} ** /apricot/app/Controllers ** namespace App\Controllers; use App\Foundation\Container; use App\Foundation\Controller; /** * Stubコントローラ */ class StubController extends Controller { /** * Stub Page * @return \Core\Foundation\Response */ public function index(int $no=null) { $title = "Stub {$no}"; /* * Example for Container * @var \App\Models\User $user */ $user = Container::get('user'); $userCount = count($user->findAll()); $messages[] = "Number of registered users : {$userCount}"; return render('stub',['title'=>$title,'messages'=>$messages]); } } * ''Container::get('user')'' でユーザモデルを生成します。 * ユーザモデルの ''findAll()'' を実行して全ユーザのリストを取得します。 * ユーザ数を表示するために、テンプレート変数 ''$messages'' をセットします。 \\ === テスト実行 === Apricotのホーム画面を表示して、[Menu2]をクリックして下さい。 [{{apricot:ext:ext09.png?nolink}}] ■ 画面にユーザ数が表示されます Number of registered users : 2 \\ \\