【Laravel】フォームバリデーションルール`image`を使用する際の注意点

## はじめに
Laravelでフォームバリデーションの`image`というルールを使っていて、今回少し分かりづらい点があったので、対処法を備忘録を残していきます。

:::note warn
今回のLaravelのバージョンは`5.6`です。
※今回の件はバージョン8.0から改善されていそうで、`7.0`までは該当するかと思います。
:::

現状のコードはこんな感じ。
```PHP:fileUploadRequest.php
$rules['file'] = 'file|image';
```

## エラー内容
- 本来jpgの画像はバリデーションが通らなければいけないが、jpgの画像をアップロードしようとすると`The upload file must be an image.`と表示されてしまっていて、pngでは問題なくバリデーション通っている。
- エラー内容概説
    - アップロードファイルは画像である必要がある。

## 試したこと(現状)
- [Laravel公式ドキュメント](https://readouble.com/laravel/5.6/ja/validation.html#:~:text=%E8%A9%95%E4%BE%A1%E3%81%97%E3%81%BE%E3%81%99%E3%80%82-,image,-%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E3%81%A7%E6%8C%87%E5%AE%9A)を確認してみる
    - フィールドで指定されたファイルが画像(`jpg`、png、bmp、gif、svg)であることをバリデートしますと書いている
    - バージョンアップしたばかりだったため、前のバージョンと見比べても挙動の違いはなさそう


ふむふむ、jpgが入っているではないか・・・
ただ、それ以上の情報が公式ドキュメントに書いていないため困りました。

先輩エンジニアにアドバイス頂き、こーゆうときはvendorの中身(ライブラリの実装コード)を見ていこうと言われ、`image`ルールの実装がLaravel側でどう判定されているのか中身を見ていき原因を特定しました。


## 原因
結論、ルール`image`だとjpgの拡張子が許容されていなかったです。
細かく見ていくと、、、

  - ルール一覧が実装されていた、下記のtraitファイルの中身を見ていくと`mimes(validateMimes())`のルールが内部で呼ばれていて、第三引数には、`jpeg`は入っているが、`jpg`が入っていない・・・
 ```PHP:vendor/laravel/framework/src/Illuminate/Validation/Concerns/ValidatesAttributes.php
    /**
     * Validate the MIME type of a file is an image MIME type.
     *
     * @param  string  $attribute
     * @param  mixed   $value
     * @return bool
     */
    public function validateImage($attribute, $value)
    {
        return $this->validateMimes($attribute, $value, ['jpeg', 'png', 'gif', 'bmp', 'svg']);
    }
```

公式には`jpg`って書いてたのに、なぜ`jpeg`の方しかないんや。。。

- mimesのルールも確認してみると許容する拡張子を明示的に指定できるようです。
    - [mimesのルール詳細](https://readouble.com/laravel/5.6/ja/validation.html#:~:text=%E7%95%B0%E3%81%AA%E3%82%8A%E3%81%BE%E3%81%99%E3%80%82-,mimes%3Afoo%2Cbar%2C...,-%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E3%81%A7%E6%8C%87%E5%AE%9A)

`mimes`ルールで明示的`jpeg`と`jpg`をどちらも指定すれば解決しそうだが、確証を得るためにvendor内下記の`validateMimes()`のバリデーション判定で、`image`と`mimes:jpg,jpeg`ルールをセットしてデバッグして比較してみる。

```diff_PHP:vendor/laravel/framework/src/Illuminate/Validation/Concerns/ValidatesAttributes.php
    /**
     * Validate the guessed extension of a file upload is in a set of file extensions.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @param  array   $parameters
     * @return bool
     */
    public function validateMimes($attribute, $value, $parameters)
    {
+       dd($value, $parameters);
        if (! $this->isValidFileInstance($value)) {
            return false;
        }

        if ($this->shouldBlockPhpUpload($value, $parameters)) {
            return false;
        }

        return $value->getPath() !== '' && in_array($value->guessExtension(), $parameters);
    }
```


**デバッグ結果**
- 変更前(image)
    - `jpeg`が許容されていて、`jpg`が何故かいない(公式の表記わかりづらい、、、)
```PHP
^ array:5 [
  0 => "jpeg"
  1 => "png"
  2 => "gif"
  3 => "bmp"
  4 => "svg"
]
```

- 変更後(mimes:jpg,jpeg,png)
    - `jpg`と`jpeg`を明示的に追加しているので、どちらも許容される
```PHP
^ array:6 [
  0 => "jpg"
  1 => "jpeg"
  2 => "png"
]
```

## 解決策
- `mimes`ルールで明示的に許容する

```diff_PHP:fileUploadRequest.php
- $rules['file'] = 'file|image';
+ $rules['file'] = 'file|mimes:jpg,jpeg,png';
```

## 結果
- 無事jpgファイルをアップロードして、バリデーションが通りました！

## 終わりに
公式ドキュメントを見ても、解決しないときはライブラリの中身の実装を見るのはかなり成長するし、いい体験になるし、課題解決力も上がる気がするので、またこの体験は別途記事にしたいと思います。

なおLaravel8からは、`image`ルールに`jpeg`が追加されている模様なので、`jpg`は元々ある点だけ気になりますが、問題なく動くかも？
もし問題あればまたライブラリ内の実装を確認するのがよいかもです。


## 参考

https://readouble.com/laravel/5.6/ja/validation.html