Laravelでcsvファイルを簡単インポート

## はじめに
選択したCSVファイルがインポートされるまでの流れを簡単に作ってみたいと思い、必要最低限で実装。

## テーブル定義
| カラム | 型 | CSVファイル読み込み対象 |
| ---- | ---- | ---- |
| id |  bigint(20)  |  |
| name | varchar(255)   | ○ |
| age | varchar(255)   | ○ |
| address | varchar(255)   | ○ |
| created_at | timestamp   |  |
| rpdated_at | timestamp   |  |


### 今回使うcsvファイル
![名称未設定のデザイン (33).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2634151/4748f610-bc1f-ce03-dfac-838c92985a99.png)


## 実際の動き

### インポート画面
![スクリーンショット 2023-11-29 21.31.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2634151/e94996bb-46cc-5a59-2d7d-50c4e55f3b6e.png)



### 「CSVファイル選択」を押下し、test.csvを開く。
![スクリーンショット 2023-11-29 21.51.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2634151/99635ec1-882e-c506-b436-415ed4478888.png)


### 「インポート」ボタンを押下する。
※選択したファイル名を表示させたい場合はjqueryなどで実装。今回は省略
![スクリーンショット 2023-11-29 21.31.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2634151/9b008eaa-46bb-9419-cd0c-75b8218cc192.png)


### csvファイルの内容がインポートされる。
![スクリーンショット 2023-11-29 22.05.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2634151/7d2594b2-c2a9-0c33-84a3-b04f6c42e7d3.png)


## 実際の処理
```html
<!DOCTYPE html>
<html>
    <head>
        <title>Laravel CSVインポート</title>
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
    </head>
    <body>
        <div class="container mt-5">
            <form action="{{ route('csvImport') }}" method="POST" enctype="multipart/form-data">
                @csrf
                <div class="form-group mb-4" style="max-width: 500px; margin: 0 auto;">
                    <div class="custom-file text-left">
                        <input type="file" name="csvFile" class="" id="csvFile">
                        <label class="custom-file-label" for="customFile">CSVファイル選択</label>
                    </div>
                </div>
                <button class="btn btn-primary btn-lg">インポート</button>
            </form>
        </div>
    </body>
</html>

```

ファイルを送る際は、enctype="multipart/form-data"と、<input type="file" が必要

idにcsvFileを指定する。コントローラーで使用するhasFile()では、指定したidを参照するので、ファイル有無のバリデーションに活用できる。

## コントローラーの処理
```php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;


class UserController extends Controller
{
    public function import(){
        return view('csvImport');
    }
    
    public function csvImport(Request $request)
    {
        if ($request->hasFile('csvFile')) {
            // リクエストからファイルを取得
            $file = $request->file('csvFile');
            $path = $file->getRealPath();
            // ファイルを開く
            $fp = fopen($path, 'r');
            // ヘッダー行をスキップ
            fgetcsv($fp);
            // 1行ずつ読み込む
            while (($csvData = fgetcsv($fp)) !== FALSE) {
                $this->InsertCsvData($csvData);
            }
            // ファイルを閉じる
            fclose($fp);
        } else {
            throw new Exception('CSVファイルの取得に失敗しました。');
        }
    }

    public function InsertCsvData($csvData)
    {
        // csvファイル情報をインサートする
        $user = new User;
        $user->name = $csvData[0];
        $user->age = $csvData[1];
        $user->address = $csvData[2];
        $user->save();
    }
    
}

```

まずはrequestにファイルが送られてきているか判定。
requestのファイルを参照する。
fopenのmode'r'、読み込みモードでCSVをオープンにする。
fopenのmodeについては下記を参照。（よく使いそうなもの。細かく知りたい場合はPHPマニュアルを確認）
| mode | 説明 | ファイルポインタ |
| ---- | ---- | ---- |
| 'r' |  読み込みのみでオープ。  | 先頭に置く |
| 'r+' | 読み込み/書き出し用でオープン   | 先頭に置く |
| 'w' | 書き出しのみでオープン   | 先頭に置く |
| 'a' | 書き出しのみでオープン   | 終端に置く |

fgetcsv関数で、csvファイルを一行ずつ配列として取得。

csvファイルを配列として取得できたので、あとはInsertCsvDataメソッドにてインサート。

業務ではバリデーションなどで複雑だと思いますが、インポートだけに着目するとシンプルだと思いました。