6
3

More than 3 years have passed since last update.

Laravel 6.x 【ファイルアップロード】 バリデーションのfileとisValidメソッドの違いを検証->必ず存在確認が必要!

Posted at

制作環境

Windows 10
Laravel : 6.18.35
Laravel/ui : 1.0
Laravel-mix : 5.0.1
Bootstrap : 4.0.0
MDBootstrap : 4.19.1
chart.js : 2.9.3
XAMPP
PHP : 7.4.3
Visual Studio Code

はじめに

この記事はプログラミングをはじめたばかりの素人が、できたことをメモするのに利用しています。
内容には誤りがあるかもしれません。

検証することにした経緯

とあるサイトで以下のような記述がありました(一部抜粋)。

public function upload(Request $request)
    {
        $this->validate($request, [
            'file' => [
                // 必須
                'required',
                // アップロードされたファイルであること
                'file',
                // 画像ファイルであること
                'image',
                // MIMEタイプを指定
                'mimes:jpeg,png',
            ]
        ]);

        if ($request->file('file')->isValid([])) {
            $path = $request->file->store('public');

            $file_name = basename($path);
            $user_id = Auth::id();
            $new_image_data = new Image();
            $new_image_data->user_id = $user_id;
            $new_image_data->file_name = $file_name;

            $new_image_data->save();

            return redirect('/output');
        } else {

画像のアップロードについて検索している時に見つけたのですが、バリデーションルールのfileとisValidが同じ事をしているように思え、片方は記述が不要なのではないかと思い、検証してみることにしてみました。

リファレンスの説明

バリデーションルールのfileについて

バリデーション.jpg

isValidメソッドについて

リファレンス.jpg

検証前の自分の見解

バリデーションでフィールドがアップロードに成功したことの確認が取れたということは、(アップロードされているから)ファイルが存在している。

アップロードに成功しているので、問題なくアップロードができている。

故に、両方とも同じ内容で片方は不要なのではないか?

そもそもの誤解

検証中に気づいたのですが、リファレンスの内容の解釈に誤解がありました。
isValidについて、リファレンスでは「ファイルが存在しているかに付け加え、isValidメソッドで問題なくアップロードできたのかを確認できます。」と記載があった為、てっきり「ファイルの存在確認+問題なくアップロードできたか確認」の両方をやっていると思ったのですが、実際はファイルの存在確認の記述にプラス(付け加えて)isValidを記述すれば、問題なくアップロードできたかの確認もできるという内容でした。

検証にあたり

ビューはアップロードのフォームとエラー・成功時のメッセージがでるだけの簡単なものを作成し、コントローラーの記述を変更することで検証していきました。
検証に直接関係ないのでビューは記載しません。
画像のアップロードを想定しています。

ビューのイメージ

image.jpg

検証

検証① バリデーションfileのみ記載 画像に対する処理なし

上記の条件で、ファイルを選択しない(空:null)状態で送信ボタンをクリック。

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\FileImage;

class FileUpController extends Controller
{
    public function index()
    {
        $data = FileImage::all();
        return view('file', compact('data'));
    }

    public function store(Request $request)
    {
        $this->validate($request, [
            'img' => 'file',
        ]);
        return redirect('/file')->with('success', '登録完了しました。');
    }
}

結果:エラーなし。「登録完了しました。」が表示される。

★注意点★

fileのみの記載だと、バリデーションは全く機能しません。
ファイルを選択し、送信をクリックした場合も結果は同じでした。
fileはあくまでも、アップロードに成功したかどうかだけを検証しているようで、アップロードの操作が内部で行われていなけば、全く機能しません。

ファイル選択なし⇒ファイルがないので、アップロードの操作なし⇒file機能せず⇒バリデーション通過
ファイル選択あり⇒ファイルがあるので、アップロードの操作あり⇒fileバリデーション実行⇒バリデーション通過

つまり、必ずファイル自体の存在確認(require等)が必須です。

検証② バリデーションfileのみ記載 画像に対する処理あり

上記の条件で、ファイルを選択しない(空:null)状態で送信ボタンをクリック。

    public function store(Request $request)
    {
        $this->validate($request, [
            'img' => 'file',
        ]);

        $path = $request->img->store('public/images');
        $filename = basename($path);

        $data = new FileImage;
        $data->file_name = $filename;
        $data->save();

        return redirect('/file')->with('success', '登録完了しました。');
    }

結果:エラー発生「Call to a member function store() on null」。
ファイルの存在確認をしていない為、ファイルが無い(空:null)時の処理でエラーが発生する。
※そもそもこういう処理の書き方はしないと思いますが、細かな動作を確認したい為、わざとやってます。

検証①の結果の通り、fileのみの記載はほぼザルなので、やはり必ずファイル自体の存在確認が必須です。

検証③ isValidメソッドのみ記載 画像に対する処理なし

上記の条件で、ファイルを選択しない(空:null)状態で送信ボタンをクリック。

    public function store(Request $request)
    {
        if ($request->file('img')->isValid()) 
            return redirect('/file')->with('success', '登録完了しました。');
        }
        return redirect('/file')->with('success', '失敗しました。');
    }

結果:エラー発生「Call to a member function isValid() on null」。
メソッド自体が機能せずエラーが発生。

やはり、必ずファイル自体の存在確認が必須です。

★注意点★

同じ条件でファイルを選択した場合は、問題無く動作し、「登録完了しました。」が表示されます。

検証④ isValidメソッドのみ記載 画像に対する処理あり

上記の条件で、ファイルを選択しない(空:null)状態で送信ボタンをクリック。

結果:エラー発生「Call to a member function isValid() on null」。
そもそもファイルが無いと、検証③で試した通りisValidがエラーになるので、検証するまでもない。
なので、コントローラの記述は掲載しません。

★注意点★

同じ条件でファイルを選択した場合は、問題無く動作し、「登録完了しました。」が表示されます。

検証⑤ バリデーションfileとisValidメソッド両方記載 画像に対する処理なし

上記の条件で、ファイルを選択しない(空:null)状態で送信ボタンをクリック。

    public function store(Request $request)
    {
        $this->validate($request, [
            'img' => 'file',
        ]);

        if ($request->file('img')->isValid()) {
            return redirect('/file')->with('success', '登録完了しました。');
        }
        return redirect('/file')->with('success', '失敗しました。');
    }

結果:エラー発生「Call to a member function isValid() on null」。
fileはザルの状態なので、検証③と同じ結果になります。
結果が検証③と同じなので、画像処理ありの検証は、検証④の理由から行いませんでした。

ここまでの結果だと、ファイルの存在確認をしていなければ、バリデーションのfileは無意味。

★注意点★

同じ条件でファイルを選択した場合は、問題無く動作し、「登録完了しました。」が表示されます。

ファイルの存在を確認しての追加検証

検証① バリデーションrequiredとfileのみ記載 画像に対する処理なし

上記の条件で、ファイルを選択しない(空:null)状態で送信ボタンをクリック。

    public function store(Request $request)
    {
        $this->validate($request, [
            'img' => 'required|file',
        ]);
        return redirect('/file')->with('success', '登録完了しました。');
    }

結果:問題なし。バリデーションのエラーがきちんと表示される。

★注意点★

同じ条件でファイルを選択した場合は、問題無く動作し、「登録完了しました。」が表示されます。

検証② バリデーションrequiredとfileのみ記載 画像に対する処理あり

上記の条件で、ファイルを選択しない(空:null)状態で送信ボタンをクリック。

    public function store(Request $request)
    {
        $this->validate($request, [
            'img' => 'required|file',
        ]);

        $path = $request->img->store('public/images');
        $filename = basename($path);

        $data = new FileImage;
        $data->file_name = $filename;
        $data->save();

        return redirect('/file')->with('success', '登録完了しました。');
    }

結果:問題なし。バリデーションのエラーがきちんと表示される。
requiredがしっかり動作しているので、ファイルが無い場合も問題無し。

★注意点★

同じ条件でファイルを選択した場合は、問題無く動作し、「登録完了しました。」が表示されます。

検証③ hasFileとisValidメソッドのみ記載 画像に対する処理なし

上記の条件で、ファイルを選択しない(空:null)状態で送信ボタンをクリック。

    public function store(Request $request)
    {
        if ($request->hasFile('img')) {
            if ($request->file('img')->isValid()) {
                return redirect('/file')->with('success', '登録完了しました。');
            }
        }
        return redirect('/file')->with('success', '失敗しました。');
    }

結果:問題なし。「失敗しました」が表示される。
ファイルの存在確認を行っているので、Call to a member function isValid() on nullのエラーは発生しませんでした。

★注意点★

同じ条件でファイルを選択した場合は、問題無く動作し、「登録完了しました。」が表示されます。

ちなみに、ネストさせず早期リターンさせるなら以下の記述でも同様の結果になります。
※「!」が入るとわかりづらい人もいるかと思うので、ネストさせる方で記載してます。

    public function store(Request $request)
    {
        if (!$request->hasFile('img')) {
            return redirect('/file')->with('success', '失敗しました。');
        }

        if ($request->file('img')->isValid()) 
            return redirect('/file')->with('success', '登録完了しました。');
        }
    }
}

検証④ hasFileとisValidメソッドのみ記載 画像に対する処理あり

上記の条件で、ファイルを選択しない(空:null)状態で送信ボタンをクリック。

    public function store(Request $request)
    {
        if ($request->hasFile('img')) {
            if ($request->file('img')->isValid()) {
                $path = $request->img->store('public/images');
                $filename = basename($path);

                $data = new FileImage;
                $data->file_name = $filename;
                $data->save();

                return redirect('/file')->with('success', '登録完了しました。');
            }
        }
        return redirect('/file')->with('success', '失敗しました。');
    }

結果:問題なし。「失敗しました」が表示される。
ファイルの存在確認を行っているので、Call to a member function isValid() on nullのエラーは発生しませんでした。

★注意点★

同じ条件でファイルを選択した場合は、問題無く動作し、「登録完了しました。」が表示されます。

検証⑤ バリデーションrequiredとfileとisValidメソッドを記載 画像に対する処理なし

上記の条件で、ファイルを選択しない(空:null)状態で送信ボタンをクリック。

    public function store(Request $request)
    {
        $this->validate($request, [
            'img' => 'required|file',
        ]);

        if ($request->file('img')->isValid()) {
            return redirect('/file')->with('success', '登録完了しました。');
        }
        return redirect('/file')->with('success', '失敗しました。');
    }

結果:問題なし。バリデーションのエラーがきちんと表示される。
requiredがしっかり動作しているので、ファイルが無い場合も問題無し。

★注意点★

同じ条件でファイルを選択した場合は、問題無く動作し、「登録完了しました。」が表示されます。

検証⑥ バリデーションrequiredとfileとisValidメソッドを記載 画像に対する処理あり

検証④で問題がない為、検証の必要がないのでこの検証は行いませんでした。
結果は検証④と同じです。

検証⑦ バリデーションfileとhasFileとisValidメソッドを記載 画像に対する処理なし

上記の条件で、ファイルを選択しない(空:null)状態で送信ボタンをクリック。

    public function store(Request $request)
    {
        $this->validate($request, [
            'img' => 'file',
        ]);

        if ($request->hasFile('img')) {
            if ($request->file('img')->isValid()) {
                return redirect('/file')->with('success', '登録完了しました。');
            }
        }
        return redirect('/file')->with('success', '失敗しました。');
    }

結果:問題なし。「失敗しました」が表示される。
ファイルの存在確認を行っているので、Call to a member function isValid() on nullのエラーは発生しませんでした。

バリデーションfileはここではザルの状態なので、この記述であれば検証③の結果も踏まえてfileは不要と言えます

★注意点★

同じ条件でファイルを選択した場合は、問題無く動作し、「登録完了しました。」が表示されます。
結果が見えているので、「画像に対する処理あり」は検証しません。
結果は同じになります。

検証結果まとめ

・fileもisValidも、単体の使用では意味がない。
・ファイルの存在確認が必須。
・存在確認をしない場合、isValidはエラーが出るが、fileはエラーもでずザルなので不要だと思う。※断定できない点があります。
・バリデーションでrequiredとfileを設定するなら、isValidは不要だと思う。※断定できない点があります。
・メソッドでhasFileとisValidを使うなら、fileは不要だと思う。※断定できない点があります。

断定できない点について

「不要。」と断定で記載したのですが、自分の現知識では検証できない内容がある為、断定ができません。
検証過程で、fileもisValidもファイルの存在確認ではなく、アップロードの処理がされたがどうかを確認していることがわかりました。

自分が検証できない内容ですが、ファイルをきちんと選択して送信をした場合で、アップロード時に何らかの障害がでた場合のそれぞれの検証です。

アップロード時に、わざと障害を起こさせることができませんでした。

仮に起こせたとして、fileもisValidも同じようにエラーを返すなら、片方だけでいいと思います。
もし、それぞれ違う結果がでるなら、両方の記載が必要。
※ただ、バリデーションでエラーを返すのと、メソッドでエラーを返すのどっちがいいか迷いますね。

結論

懸念材料がある為、両方記載するのが現状望ましい。
存在の確認についてはrequiredかhasFileのどちらかでいい。
ただし、hasFileを使用するのであれば、存在確認をしないfileはザルなので、fileは不要。

素人の個人的な見解なので、参考程度にしておいてください。

6
3
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
6
3