まえがき
最近はLaravelを触ってます。
で、バリデーションまわりを触っていたのだけど、ファイルアップロードで困ったことが。
エラーがあって戻されたときに画像ファイルの情報が消し飛んでしまって何も分からない。。
「ファイル残してほしい」とは言わないけど、
せめてどこのファイルをあげようとしていたのか教えてほしいなぁ、と思ったので対応したよ。
前提
- PHP7.3.6
- Laravel 5.5.45
なぜ消えるのか
バリデーションエラーが発生した場合、自動で前ページに戻される。
その際に入力時のデータもセッション内に保存してくれているのだけど、この部分で意図的に消しているからだった。
ExceptionHandler
正確には Illuminate\Foundation\Exceptions\Handler
。
ValidationException発生時、なにもしていなければこのクラスの render
に入ってきて、
convertValidationExceptionToResponse
からの invalid
へ渡す。
この invalid
で前ページに戻る処理をしているのだけど、
その際に渡す入力元データをリクエストを元に生成している。
redirect
正確には Illuminate\Http\RedirectResponse
。
この中の withInput
からの removeFilesFromInput
で
protected function removeFilesFromInput(array $input)
{
foreach ($input as $key => $value) {
if (is_array($value)) {
$input[$key] = $this->removeFilesFromInput($value);
}
if ($value instanceof SymfonyUploadedFile) {
unset($input[$key]);
}
}
return $input;
}
SymfonyUploadedFile
のインスタンスをバッサリ削除してた。
そりゃ何も取れない訳だ・・・・('A`)
SymfonyUploadedFileを消させない
こんな根っこから対応が入ってるんじゃ手を入れづらい・・・・と思ったけど、そんなことはなく。
ExceptionHandler::convertValidationExceptionToResponse
を見直す。
protected function convertValidationExceptionToResponse(ValidationException $e, $request)
{
if ($e->response) {
return $e->response;
}
return $request->expectsJson()
? $this->invalidJson($request, $e)
: $this->invalid($request, $e);
}
引数である ValidationException $e
にレスポンスが入っていれば、それ以降の処理はせず、直接使う。
てことは、ここより前に $e->response
をつけてやればいい。
protected function convertValidationExceptionToResponse($e, $request)
{
$url = $exception->redirectTo ?? url()->previous();
$inputs = $request->all();
foreach ($inputs as $key => $value) {
if (is_array($value)) {
$inputs[$key] = $this->removeFilesFromInput($value);
}
if ($value instanceof SymfonyUploadedFile) {
// 元ファイル名だけ返す
$inputs[$key] = basename($value->getClientOriginalName());
}
}
$e->response = redirect($url)
->withInput($inputs)
->withErrors(
$e->errors(),
$e->errorBag
);
return parent::convertValidationExceptionToResponse($e, $request);
}
ファイル名くらいはあってもいいかな、と思って入れたけど、
別に「あげ直してね」ていうくらいならTRUE/FALSEでもいいね。。
<table>
<th>画像ファイル</th>
<td>
{{ Form::file('image') }}
@if ($errors->has('image'))
<p>{{ $errors->first('image') }}</p>
@elseif ($errors->any())
<p>ファイルをあげ直してください: {{ old('image') }}</p>
@endif
</td>
</table>
テンプレートが雑なのはご愛嬌。。
10個くらいフォーム作って出した方が分かりやすいかとは思ったんだけど、メンドかった(´・ω・`)
あとがき
ちょっと調べるのに時間がかかったのでメモ的に。
とはいえ、Laravel触り始めだから、基底クラスをめぐるちょうどいい機会ではあったかな(^-^)
まぁ、需要があるかは分からないけど。。。