LaravelのFormRequestの仕組みを調べてみたので備忘録。 Laravelのバージョンは11.0.7です。
ServiceProviderでのコールバック関数の設定
ServiceProviderでFormRequestのインスタンス化とバリデーションする処理の追加をしています。
Illuminate\Foundation\Providers\FoundationServiceProvider経由でIlluminate\Foundation\Providers\FormRequestServiceProviderが呼ばれます。
/**
* Bootstrap the application services.
*
* @return void
*/
public function boot()
{
$this->app->afterResolving(ValidatesWhenResolved::class, function ($resolved) {
$resolved->validateResolved();
});
$this->app->resolving(FormRequest::class, function ($request, $app) {
$request = FormRequest::createFrom($app['request'], $request);
$request->setContainer($app)->setRedirector($app->make(Redirector::class));
});
}
Container::resolving()
では $resolvingCallbacks
プロパティにコールバック関数がセットされます。
/**
* Register a new resolving callback.
*
* @param \Closure|string $abstract
* @param \Closure|null $callback
* @return void
*/
public function resolving($abstract, ?Closure $callback = null)
{
if (is_string($abstract)) {
$abstract = $this->getAlias($abstract);
}
if (is_null($callback) && $abstract instanceof Closure) {
$this->globalResolvingCallbacks[] = $abstract;
} else {
$this->resolvingCallbacks[$abstract][] = $callback;
}
}
https://github.com/laravel/framework/blob/11.x/src/Illuminate/Container/Container.php#L1163-L1174
Container::afterResolving()
では $afterResolvingCallbacks
プロパティにコールバック関数がセットされます。
/**
* Register a new after resolving callback for all types.
*
* @param \Closure|string $abstract
* @param \Closure|null $callback
* @return void
*/
public function afterResolving($abstract, ?Closure $callback = null)
{
if (is_string($abstract)) {
$abstract = $this->getAlias($abstract);
}
if ($abstract instanceof Closure && is_null($callback)) {
$this->globalAfterResolvingCallbacks[] = $abstract;
} else {
$this->afterResolvingCallbacks[$abstract][] = $callback;
}
}
https://github.com/laravel/framework/blob/11.x/src/Illuminate/Container/Container.php#L1183-L1194
HTTPリクエスト時のインスタンス化
Container::make()
すると Container::resolve()
が呼び出されます。
protected function resolve($abstract, $parameters = [], $raiseEvents = true)
{
// ...(省略)...
$object = $this->isBuildable($concrete, $abstract)
? $this->build($concrete)
: $this->make($concrete);
// ...(省略)...
if ($raiseEvents) {
$this->fireResolvingCallbacks($abstract, $object);
}
// ...(省略)...
return $object;
}
https://github.com/laravel/framework/blob/11.x/src/Illuminate/Container/Container.php#L755-L816
fireResolvingCallbacks()
は resolvingCallbacks
afterResolvingCallbacks
にセットされたコールバックを呼び出します。
protected function fireResolvingCallbacks($abstract, $object)
{
$this->fireCallbackArray($object, $this->globalResolvingCallbacks);
$this->fireCallbackArray(
$object, $this->getCallbacksForType($abstract, $object, $this->resolvingCallbacks)
);
$this->fireAfterResolvingCallbacks($abstract, $object);
}
https://github.com/laravel/framework/blob/11.x/src/Illuminate/Container/Container.php#L1236-L1245
resolvingCallbacksでインスタンス化したFormRequestにRequestクラスから値が設定されます。 https://github.com/laravel/framework/blob/11.x/src/Illuminate/Http/Request.php#L439-L472
FormRequestクラスはValidatesWhenResolvedインターフェースを実装しているので、ValidatesWhenResolved::classで設定した処理が呼ばれ、 validateResolved()
メソッドが叩かれます。
https://github.com/laravel/framework/blob/11.x/src/Illuminate/Container/Container.php#L1276
validateResolved()
は以下のような処理になっています。
/**
* Validate the class instance.
*
* @return void
*/
public function validateResolved()
{
$this->prepareForValidation();
if (! $this->passesAuthorization()) {
$this->failedAuthorization();
}
$instance = $this->getValidatorInstance();
if ($this->isPrecognitive()) {
$instance->after(Precognition::afterValidationHook($this));
}
if ($instance->fails()) {
$this->failedValidation($instance);
}
$this->passedValidation();
}
fails() => passes() という感じで呼び出され、rulesを使ったバリデーションが行われ、バリデーションエラーは $messages
プロパティにセットされます。
https://github.com/laravel/framework/blob/v11.0.7/src/Illuminate/Validation/Validator.php#L436-L483