7
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【Laravel】入力エラーのとき「ファイルをあげ直してね」メッセージを出すよう対応したよ

Posted at

まえがき

最近はLaravelを触ってます。

で、バリデーションまわりを触っていたのだけど、ファイルアップロードで困ったことが。
エラーがあって戻されたときに画像ファイルの情報が消し飛んでしまって何も分からない。。
「ファイル残してほしい」とは言わないけど、
せめてどこのファイルをあげようとしていたのか教えてほしいなぁ、と思ったので対応したよ。

前提

  • PHP7.3.6
    • Laravel 5.5.45

なぜ消えるのか

バリデーションエラーが発生した場合、自動で前ページに戻される。
その際に入力時のデータもセッション内に保存してくれているのだけど、この部分で意図的に消しているからだった。

ExceptionHandler

正確には Illuminate\Foundation\Exceptions\Handler
ValidationException発生時、なにもしていなければこのクラスの render に入ってきて、
convertValidationExceptionToResponse からの invalid へ渡す。

この invalid で前ページに戻る処理をしているのだけど、
その際に渡す入力元データをリクエストを元に生成している。

redirect

正確には Illuminate\Http\RedirectResponse
この中の withInput からの removeFilesFromInput

Illuminate\Http\RedirectResponse
    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 を見直す。

Illuminate\Foundation\Exceptions\Handler
    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 をつけてやればいい。

\App\Exceptions\Handler
    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触り始めだから、基底クラスをめぐるちょうどいい機会ではあったかな(^-^)

まぁ、需要があるかは分からないけど。。。

7
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?