Laravelのサービスプロパイダでハマった話
最近laravel(Laravel8)のサービスプロパイダの存在を知ったのですがそのときにハマった話、自分が行った解決策を紹介したいと思います。
最終的には自分がしたかったことをでき、問題の解決ができていますが間違い、更なる改善ができるような箇所があれば指摘いただけると幸いです。
状況
状況としてサービスプロパイダで呼び出したクラスでユーザー情報を扱った処理を行おうとしていました。サービスプロパイダ内ではそのクラスをそのクラスのインターフェースとbind関数を使って紐づけており
$this->app->bind(\App\Service\UserServiceInterface::class, \App\Service\Production\UserService::class);
コントローラーではメソッドインジェクションを行って使用していました。
public function index(UserServiceInterface $user)
{
...
$user_info = $user-関数(引数);
...
}
その時の呼び出したクラス(ここでは↑で書かれたものを呼び出しているとするとUserServiceクラス 以下でも同じものを使います)ではコンストラクタ内でauthを直接呼び出していました。
$this->auth = Auth::user();
ミドルウェアから呼び出そうとしてもなぜかnullしか取得できなかったのでこちらの方法で取得しています。この時は問題なく動いているのですがどうしてもコントローラー内ではメソッドインジェクションではなくコンストラクタインジェクションを行いたいと思っていました。なのでコンストラクタインジェクションに書き換えようとしたのですがなぜかUserService内でauthの情報を取得できずにnullが帰ってくる。
どうにかしてコンストラクタインジェクションにしてbindしているところもシングルトンにしたいため試行錯誤してみました。
考察
まずなぜUserServiceクラス内でミドルウェアからユーザー情報が取得できなかった(エラーが出た)かというとサービスプロパイダが呼び出されるタイミングがミドルウェアが呼び出されるタイミングより早いからです。
次になぜメソッドインジェクションの時のみ正常に実行できコンストラクタインジェクションの時はauth情報が取得できずエラーが出るかというとコントローラー内のメソッド例えばindexが呼び出された後でUserServiceクラスが呼び出され、このときにはAuthが使うことができる状態であるがコンストラクタインジェクションの時はAuthがまだ使えないためnullが返されたのだと思っています。
最後にシングルトンにしたときにもauth情報が取得できずにエラーが出た理由ですがこれは二つ目の考察とかぶるのですがサービスプロパイダが呼び出されたタイミングでUserServiceクラスが作られ、このタイミングではまだAuthが使えるようになっていない状態でクラスが作成されます。シングルトンではクラスが使いまわされるため、メソッドインジェクションを行ったところでAuthがまだ使えない状態の時に作られたクラスが渡されるためエラーが出たのだと考えられます。
結論
実際にどのようにして直したかですが関数内で使うauthの情報を個別に関数の引数として渡しました。
public function index()
{
...
$user_info = $this->user->関数(使用するauth情報+元々必要な引数);
...
}
このようにすればエラーなく動かすことができました!
インターフェース・クラスの一部を書き換えた上で、サービスプロパイダのbindもsingletonに変更し、コントローラー内のメソッドインジェクションはコンストラクタインジェクションにすることができました!
この記事が気に入ったらサポートをしてみませんか?