雑記 : Laravelの嫌いなところ
Laravelは好きだけど
Laravelは今最もトレンドなphp言語向けWebアプリケーションフレームワークで,5.1 LTSのリリース時から使い続けています。
以前使っていたZend Framework 2よりはるかに直感的に扱え,まさに「作りたい物を作りたいところだけ」作れる,そんな理想の存在がLaravelでした。
が,使い続けていくうちにだんだんと嫌なところも見えてきます。今回はそんなLaravelの嫌なところをまとめてみました。
1. Facade
が嫌い
Laravelにはまるで静的メソッドのようにインスタンスの動的なメソッドを呼び出すことの出来るFacade
という便利な仕組みがあります。
これはFacade
と呼ばれる静的クラスが,php言語のマジックメソッドを用いてDIコンテナにアクセスし,取得したインスタンスの動的メソッドを実行するというものです。
確かにこの構造は便利で,場所を気にせず好きなところでロジックを呼び出せます。
しかしこの構造はとてもいびつであり,DIコンテナとコンストラクタ・メソッドインジェクションによって下げられた疎結合性を一気に密結合なものへと変えてしまいます。
また,Facade
を用いた処理の実行は毎回マジックメソッドを経由して行われる為,実行コストも高いものとなります。そもそも強力なコンストラクタ・メソッドインジェクションがあるLaravelにおいては,Facade
の使用は必要最小限,もしくは完全に撤廃してしまっても問題ないように思います。
幸いにも,Laravel 5.5 LTSの時点ではFacade
の使用は任意であり,作成したプロジェクトのいくつかのファイルを修正することでFacade
を用いずにアプリケーションを作ることも可能です。また,そういった手法を用いることを好んでいる方も居ます(私もその一人です)
2. ヘルパ関数
が嫌い
1.のFacade
が嫌いとほぼ同じ理由です。が,これに関しては回避手段がありません。LaravelはDIコンテナに対しstringで値を登録していることが多く,そのような値を取得するには最悪でもServiceLocator アンチパターンを用いるしかなくなってしまいます。
例えば,プロジェクトのストレージパスを取得するstorage_path()
ヘルパ関数の実装がどのようになっているのか見てみましょう。
<?php // 省略 if (! function_exists('storage_path')) { /** * Get the path to the storage folder. * * @param string $path * @return string */ function storage_path($path = '') { return app('path.storage').($path ? DIRECTORY_SEPARATOR.$path : $path); } } // 省略
まずはapp()
ヘルパ関数に対し,path.storage
という文字列を与えているのがわかります。
ではapp()
ヘルパ関数の実装を見てみます。
<?php // 省略 if (! function_exists('app')) { /** * Get the available container instance. * * @param string $abstract * @param array $parameters * @return mixed|\Illuminate\Foundation\Application */ function app($abstract = null, array $parameters = []) { if (is_null($abstract)) { return Container::getInstance(); } return Container::getInstance()->make($abstract, $parameters); } } // 省略
はい,そうなのです。path.storage
という文字列を抽象として,パスという文字列を具象として登録しています。
文字列は紛れもない具象です。文字列を表す抽象は存在し得ず,またオブジェクトではない為,コンストラクタ・メソッドインジェクションのどちらも使うことが出来ません。
Laravelは多くの箇所でこのような文字列に対する具象登録を行っており,ServiceLocatorパターンを用いない限り登録された具象を取得することが出来ません。これは回避不可能です。
3. Contracts
が嫌い
Laravelは具象と抽象を区別するためにContracts
という名前空間を用いてインターフェイスを隔離しています。しかし全ての具象に対してContracts
が存在するわけではなく,またContracts
以下の名前空間の問題からas
を用いた別名定義を用いなければ使えない状況が多発しています。
例えば,Controllerのメソッドインジェクションを用いてView
とCache
のインスタンスを取得しようとすると,以下のようになります。
<?php namespace App\Http\Controllers; use Illuminate\Contracts\View\Factory as ViewFactory; use Illuminate\Contracts\Cache\Factory as CacheFactory; class TestController extends Controller { public function index(ViewFactory $view, CacheFactory $cache) { $view->make('welcome'); } }
4. Eloquent
が嫌い
嫌いというか,非常に学習コストが高いです。ActiveRecordのようにも見えますがActiveRecordにもなりきれておらず,悪く言えば中途半端,良く言えばどうとでも使えるOR Mapperになっています。
というのも,Eloquent
は前述のFacade
と親しい部分があり,それがデータベースを表すのか行を表すのかが非常に判別しにくくなっています。
具体的に言うとこんな感じになります。あまりにわかりにくいので変数名にinstance
と命名することでそれが何であるかを明確にせざるを得ませんでした。
それでもLaravelは何よりも現実的
確かに設計の面でいびつな部分も数多く存在するLaravelですが,現在存在するphp言語向けWebアプリケーションフレームワークの中では最も開発効率に優れたものであるということも間違いないであろうと思っています。
Facade
やヘルパ関数
はコードの品質を下げかねない諸刃の剣ですが,使い方を間違えさえしなければよほどのことにはなりません。仮に密結合なコードを書いてしまったとしても,LaravelのDIコンテナには後からそれを救う機能が備わっています。
結論としては,Laravelはやっぱり良いフレームワークだな,というところでした。