本記事ではLaravelのアプリケーションを理解し、より良い設計・アーキテクチャを構築できるように学習したことを簡潔にまとめています。
#目次
1.Laravelのアーキテクチャ
2.アプリケーションのアーキテクチャ
3.HTTPリクエストとレスポンス
4.データベース
5.認証と許可
6.イベントとキューによる処理の分離
7.コンソールアプリケーション
8.テスト
9.エラーハンドリングとログの活用
10.テスト駆動開発の実践
#1.Laravelのアーキテクチャ
##1-4. ファサード
ファサードは、クラスメソッド形式でフレームワークの機能を簡単に利用できるもので、Laravelを代表する機能の1つ。ファサードも裏側ではサービスコンテナの機能が使われており、本稿ではその仕組みを紹介する。
下記にConfigファサードのgetメソッドでconfig/app.phpのdebugキーの値を取得する例を示す。
$debug = \Config::get('app.debug');
<コード解説>
上記のコードでは、Configクラスにgetメソッドが実装されているように見えるが、実はConfigクラスというものは存在しない。
このクラスの招待はIlluminate\Support\Facades\Configクラスの別名であり、config\app.phpのaliasesキーの定義に従って[Config]とIlluminate\Support\Facades\Config
クラスが関連付けされている。
下記にconfig\app.phpのaliasesキーの抜粋を示す。
'aliases' => [
'App' => Illuminate\Support\Facades\App::class,
'Artisan' => Illuminate\Support\Facades\Artisan::class,
'Auth' => Illuminate\Support\Facades\Auth::class,
'Config' => Illuminate\Support\Facades\Config::class,
],
次に、Configクラスの実体であるIlluminate\Support\Facades\Configクラスのコードを示す。
<?php
namespace Illuminate\Support\Facades;
class Config extends Facade
{
protected static function getFacadeAccessor()
{
return 'config';
}
}
上記に示した通り、ConfigクラスにはgetFacadesAccessorメソッドしかなく、getメソッドは定義されていない。スーパークラスであるIlluminate\Support\Facades\Facadeクラスにもgetメソッドは存在しない。このように呼ばれたクラスメソッドが実装されていない場合、__callStaticメソッドと呼ばれるマジックメソッドが動作する。
下記に__callStaticメソッドのコード例を示す。
namespace Illuminate\Support\Facades\Facade;
public static function __callStatic($method, $args)
{
$instance = static::getFacadeRoot(); // ①
if(! $instance) {
throw new RuntimeException('A facade root has not been set');
}
return $instance->$method(...$args); // ②
}
上記のコードは、Illuminate\Support\Facades\Facadeクラスの__callStaticメソッドの処理が示されており、getメソッドが呼ばれるとこのメソッドが実行される。
$methodには実行メソッド(ここではget)、$argsには実行されたメソッドの引数を格納した配列(ここでは['app.debug'])が格納されている
①でファサードが受け持つインスタンスをサービスコンテナから取得し、②で取得したインスタンスの$method名のメソッドを実行する。
→ファサードのメソッド実行は、サービスコンテナから取得したインスタンスに対してメソッドが実行される
次に、ファサードがサービスコンテナからインスタンスを取得する処理である、static::getFacadeRootメソッドのコードを下記に示す。
public static function getFacadeRoot()
{
return static::resolveFacadeInstance(static::getFacadeAccessor());
}
protected static function getFacadeAccessor()
{
throw RuntimeException('Facade does not implement getFacadeAccessor method.');
}
protected static function resolveFacadeInstance($name);
{
if(is_object($name)) {
return $name;
}
if(isset(static::$resolvedInstance[$name])) {
return static::$resolvedInstance[$name];
}
if(static::$app) {
return static::$resolbedInstance[$name] = static::$app[$name];
}
}
<コード解説>
上記コード例に示す通り、getFacadeRootメソッドはgetFacadeAccessorメソッドから取得した値を利用して、resolveFacadeInstanceメソッドでインスタンスを取得する。(※getFacadeAccessorメソッドはオーバーライドされることを想定としている。)
resolveFacadeInstanceメソッドは、引数$nameをサービスコンテナで解決して取得したインスタンスを返す。
Illuminate\Support\Facades\Configには、getFacadeAccessorメソッドが実装されており、文字列configを返している。
→つまり、サービスコンテナで[config]クラス名で解決されたインスタンスを利用していることになる。
(static::$app[$name] = app()->make($name);となるので、一個前のコードの①の$instance = static::getFacadeRoot();は
app()->make('config');を行っているということになる)
##ファサードが動く仕組み(まとめ)
1.Config::get('app.debug')がコールされる
2.Configの実態であるIlluminate\Support\Facades\Configクラスのgetメソッドを呼び出す
3.Illuminate\Support\Facades\Configクラスにはgetメソッドがないため、スーパークラスの__callStaticメソッドを呼び出す
4.__callStaticメソッドでは、getFacadeRootメソッドで操作対象のインスタンスを取得し、getメソッドを実行する(getFacadeRootメソッドでは、getFacadeAccessorメソッドで取得した文字列をresolveFacadeInstanceメソッドによりサービスコンテナで解決し、取得したインスタンスを返す。)。
##最後に
フレームワークの機能を手軽に利用できることがファサードの利点であり、ロジックのどこからでも利用可能である。
しかし、その特性ゆえにコードの密結合を招く恐れがあります。小規模アプリケーションでは問題ないが、大規模や運用期間が長いアプリケーションではファサードを経由せずサービスコンテナから直接インスタンスを利用する方式も検討したほうがよい。