Laravel での ajax を利用したファイルアップロード

#はじめに

Laravelを使っている案件でファイルアップロードを行う機会があったので、実装方法を記します。
なぜajaxを利用したかというと、編集画面というフォームの中でファイルアップロード機能を実装したかったから（編集画面で画面遷移することなくファイルアップロードのみ行いたかったから）

## HTML
```html
<div>
    <label for="" class="">ファイルのアップロード</label>
    <div class="">
        <input type="file" class="" id="upload_file" name="upload_file" accept="pdf, xlsx" style=''>
    </div>
    <input type="button" value="アップロード" id="upload" data-toggle="modal" data-target="#file_upload_modal" class="" style=''>
</div>

@component('includes.popup_modal', ['id' => 'file_upload_modal'])
    @slot('title')
        ファイルのアップロード
    @endslot
    @slot('body')
        ファイルのアップロード
        <label style="" class="" id="searchErrorMessage"></label>
    @endslot
    @slot('footer')
        <button type="button" id="file_upload_btn" class="">アップロード</button>
        <button type="button" class="" data-dismiss="modal">キャンセル</button>
    @endslot
@endcomponent
```

アップロードボタンをクリックするとモーダルを表示して「アップロード」ボタンを選択する事でアップロード処理を行います。
モーダルは別ファイルで管理しています。

## ajax

```javascript
<script>
$(document).ready(function () {
    $('#file_upload_btn').on('click', function() {

        //ajaxでのcsrfトークン送信
        $.ajaxSetup({
            headers: {
                'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
            }
        });

        //アップロードするファイルのデータ取得
        var fileData = document.getElementById("upload_file").files[0];
        //フォームデータを作成する
        var form = new FormData();
        //フォームデータにアップロードファイルの情報追加
        form.append("file", fileData);
        $.ajax({
            type: "POST",
            url: "{{ route('ajax_file_upload', ['id' => $id']) }}",
            data: form,
            processData: false,
            contentType: false,
            success: function (response) {
                if(response.is_success) {
                    //モーダルの処理
                    //エラーメッセージ非表示
                    $('#searchErrorMessage').html('');
                    $('#searchErrorMessage').hide();
                    //モーダルを閉じる
                    $('#file_upload_modal').modal('toggle');
                } else {
                    //モーダルでのエラーメッセージ表示処理
                    var msg = "";
                    $.each(response.errors_message, function (i, v) {
                        msg += v + "<br>";
                    });
                    $('#searchErrorMessage').html(msg);
                    $('#searchErrorMessage').show();
                }
            },
            error: function (response) {
                //エラー時の処理
            }
        });
    })
})
</script>
```

```javascript
headers: {
    'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
````
ここでCSRF保護のためcsrf-tokenをヘッダーに設定します。
これを行わないとエラーになります。
あとは

```javascript
var form = new FormData();
```
フォームデータを作成してあげてこれを指定のControllerに渡します。

ここでは

```javascript
url: "{{ route('ajax_file_upload', ['id' => $id']) }}",
```
ajax_file_upload というrouteに渡してます


## PHP(LaravelのController)
Controllerでファイル情報を受け取ってサーバに保存します。

```php
public function uploadFile(Request $request, $id){
    $response = [];

    //元ファイルのファイル名取得
    $fileName = $request->file('file')->getClientOriginalName();

    //拡張子チェック
    $ext = pathinfo($fileName)['extension'];
    $check_extension = ['xlsx', 'pdf'];
    if(!in_array($ext, $check_extension, true)){
        $response = [
            'is_success' => false,
            'errors_message' => ["エクセルファイルかPDFファイルのみアップロード可能です"]
        ];
        return response()->json($response);
    }
    //ファイル保存
    $upload_file_save = config('upload');
    $request->file('file')->storeAs($upload_file_save . $id, $fileName);

    $response = [
        'is_success' => true,
        'errors_message' => [""]
    ];

    return response()->json($response);
}
```

```php
$fileName = $request->file('file')->getClientOriginalName();
```
getClientOriginalName で元のファイル名を取得できます。
※元ファイル名を利用しなかった場合はPHPが任意に決めたファイル名になります。

```php
$response = [
    'is_success' => false,
    'errors_message' => ["エクセルファイルかPDFファイルのみアップロード可能です"]
];
```
エラーが起こった際に（この際は拡張子チェックでエラー）アップロードする際に確認を行ったモーダルにエラーメッセージを返します。

```php
$upload_file_save = 'upload' . $id;
$request->file('file')->storeAs($upload_file_save, $fileName);
```
storeAs でファイル名の指定が可能です。ここではアップロードした元のファイル名。
また、アップロード先のディレクトリは指定しなければ `storage/app` となります
ここでは storage/app/upload/{$id} となります。

#さいごに
これで最低限の実装は出来る、はず、です
間違いなどありましたらご指摘ください。