BIOGRAPHYREADME2016.12.09Laravelでセッターインジェクションするこの記事はLaravelアドベントカレンダー8日目の記事です。qiita.com前提知識この記事ではDIパターンを実現する1つの手段であるセッターインジェクションをLaravelで実現する方法を紹介します。DIパターンやDIコンテナを知らない方は先にこれらの記事を読んでいただくと理解が進むと思います。パターンさくっと知りたい方は私が今年発表したスライドの55枚目までを流し読みしていただければと思います。InjectionLaravelはHTTPリクエストが来たタイミングでDIコンテナが起動します。それがアプリケーションコードのほぼ全ての依存を解決するので、開発者はインスタンスの生成方法を意識せずにコーディングすることが可能となっています。以下のようなコントローラークラスがあるとしましょう。}}なんてことはない、mainという名前のViewを必要なデータを入れてレンダリングして返すコントローラーメソッドです。このコントローラーをもしLaravelを使わずに使おうと思うとおそらくこんな感じのコードを書かなければなりません。}}注目してほしいのは連想配列の/mainに指定しているClouser部分です。実装者はSampleControllerインスタンスを実装し、mainメソッドが依存しているインスタンスを自分の手で生成する必要があります。小規模なアプリケーションであればこのような方法で実装するのは問題にはなりづらいかもしれません。クラス数が増えたら?インスタンスの生成方法が変わったら?実装の差し替えが起こったら?そういったことを考えるとこのような方法では将来的につらいことになる可能性が高いです。Laravelだとこんなことはしなくても大丈夫です。wiring先ほど述べたようにLaravelではHTTPリクエストが来た際にDIコンテナが立ち上がります。そのDIコンテナがアプリケーションコード中でDIパターンによって明示的に指定されている依存関係を全てよしなに解決します。wiringという仕組みで動いています。詳しくは下記リンクを読むと理解が進むかと思います。Containerこれによって何が嬉しいかというと、開発者は例えばSampleController::main()メソッドが必要としているSampleServiceのインスタンスを生成する必要が無いということです。普段は意識することは少ないかもしれませんが、これを覚えておくと設定なしにInterfaceやスカラー型をタイプヒントしてもDIコンテナからインジェクションしてくれない理由がわかると思います。セッターインジェクションセッターインジェクションとはDIパターンを実現するための1つの手段です。Laravelでよく使われる手段としてコンストラクタインジェクションがあります。}}これをセッターメソッドを用意して行うのがセッターインジェクションです。上記のコードをセッターインジェクションを用いたコードに書き直すとこんな感じ。}}こうすることで以下のような形でSampleRepositoryのインスタンスをDIすることができます。SampleRepository);簡単ですよね。Laravelでのセッターインジェクションのやり方ここからがこの記事の本編です。Laravelではセッターインジェクションに対する定義を行うAPIが用意されていません(あったら教えてください...)。方針としてはDIコンテナにより対象のクラスのインスタンスが生成される生成されたインスタンスにセッターメソッドを通してDIするといった感じでやります。対象のクラスは先ほど出てきたSampleServiceを利用します。}}このクラスをそのままLaravelで利用したら、普通に動きます。セッターは実行されないのでインスタンス生成直後にセッターメソッドを実行したい。いった場合はIlluminate/Container/Container::extendを利用します。任意のServiceProviderでこんな処理を書きます。}}また、extend等を通じて完全に依存解決された後にセッターインジェクションしたい場合にはIlluminate/Container/Container::resolvingを使用できます。}}こうすることでDIコンテナによって生成されたSampleServiceのインスタンスにセッターインジェクションを行うことができました!セッターインジェクションの利用場面このセッターインジェクションですが、普通の実装をしているとあまり使う場面は出てきません。いうのも大抵のインスタンスはコンストラクタインジェクションで事が足りるからです。この方法を覚えておくと設計の幅が広がります。インスタンス生成の方法が複雑でコンストラクタの拡張が困難な場合などはセッターを生やした継承クラスを作成し、bindした上でセッターインジェクションするといったことが可能です。他にも例えばログやトランザクションといった汎用的な処理を行うインスタンスのセッターをTraitで作成し、DIコンテナでセッターインジェクションすればクラス本体を汚さずに欲しいインスタンスを注入することも可能です。まとめLaravelの良さはDIコンテナの柔軟さだと思ってます。DIコンテナをしっかり利用できれば開発も楽になりますし何より楽しくなるので、依存解決で難解な場面に出くわすことがあればぜひこのセッターインジェクションという方法を思い出してみてください。明日の記事はIganinTeaの記事です。