FormRequestで自作のファイル拡張子バリデーションを作ってみた

## 環境
Laravel 6.20.8
PHP7.4

## はじめに
コントローラーにもバリデーションは書けますが、複雑なバリデーションになればなるほど、コントローラーが肥大化してしまうので、FormRequestクラスを活用します。
FormRequestに自作でバリデーションをどのように書くのか、画像ファイルの拡張子チェックを例にします。

Laravelのバリデーションルールは豊富なので、rulesメソッドにmimesを指定してしまえば、アップロードしようとしているファイルのMIMEタイプはチェックしてくれます。ですが、MIMEタイプも改ざんできてしまうので、同時に拡張子のチェックもしたいと思います。

## 準備
下記コマンドで、App\Http\Requests配下にFormRequestクラスを生成します。

```
php artisan make:request CreateItemRequest
```

すると、下記のようなファイルが作られます。authorize()は、認証関係の判定の処理を書きますが、今回は不要なのでtrueを返すようにします。バリデーションはrules()に書いていき、前述の通りmimesを指定します。（今回はjpgとpngのみにします）

```php:app/Http/Requests/CreateItemRequest.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class CreateItemRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules() 
    {
        'name' => 'required|string|max:255',
        'picture_name' => 'required|file|mimes:jpg,png', 
    }

}
```


コントローラーの変更点は2点です。作成したFormRequestクラスをuseして、store()などPOST送信を受けるメソッドの引数に、作成したcreateItemクラスを渡すだけです。

```php:ItemController.php

//中略

use App\Http\Requests\CreateItemRequest;

//中略

public function store(CreateItemRequest $request)
{
	//
}

```

## 自作で拡張子バリデーションを作成する

下記にバリデーションを書いていきます。withValidatorメソッドは、rules()で指定したバリデーション評価前に実行されるようです。

```php:app/Http/Requests/CreateItemRequest.php

public function withValidator($validator)
{
	//ここにバリデーションを書いていきます
}

```
```php:app/Http/Requests/CreateItemRequest.php
public function withValidator($validator)
{
    $validator->after(function ($validator)
    {
        $file_data = $this->file('picture_name');
        $file_extension = $file_data->getClientOriginalExtension(); //拡張子取得
        $lower_case_extension = strtolower($file_extension); //拡張子を小文字に変換

        if($lower_case_extension !== 'jpg' && $lower_case_extension !== 'png'){
            $validator->errors()->add('', 'アップロードされたファイルは画像ファイルではありません。');
        }
    });
}
```
拡張子は、大文字でも小文字でもチェックできるよう、一度拡張子を小文字に変換します。エラー時のメッセージは直接書きます。

## 最終的なファイル
```php:app/Http/Requests/CreateItemRequest.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class CreateItemRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules() 
    {
        'name' => 'required|string|max:255',
        'picture_name' => 'required|file|mimes:jpg,png', 
    }

    public function withValidator($validator)
   {
        $validator->after(function ($validator)
        {
            $file_data = $this->file('picture_name');
            $file_extension = $file_data->getClientOriginalExtension(); //拡張子取得
            $lower_case_extension = strtolower($file_extension); //拡張子を小文字に変換

            if($lower_case_extension !== 'jpg' && $lower_case_extension !== 'png'){
                $validator->errors()->add('', 'アップロードされたファイルは画像ファイルではありません。');
            }
        });
    }

}
```

## おわりに
もともとLaravelが用意してくれているバリデーションルールを使えば、かなりのバリデーションができると思いますが、独自のバリデーションが必要なこともあると思うので、withValidatorメソッドの使い方のメモを残しました。

## 参考
https://readouble.com/laravel/6.x/ja/validation.html
https://www.ritolab.com/entry/41