LoginSignup
3
2

More than 1 year has passed since last update.

【Laravel】SplFileObjectでCSVファイルのデータを読み込んでDBに反映する

Last updated at Posted at 2022-03-21

サンプル

入力画面

image.png

コード

ルーティング処理

\routes\web.php
Route::get('/csv/','Csv\CsvController@index')->name('csv.index');
Route::post('/csv/finish/','Csv\CsvController@finish')->name('csv.finish');

今回はviewに使うファイルが1つしかないため、name()を使って、ルーティング処理ができるようにしている。

コントローラー

  • index: 入力画面の処理
  • finish: CSVファイルデータ登録処理
\app\Http\Controllers\Csv\CsvController.php

<?php

namespace App\Http\Controllers\Csv;

use DB;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;

class CsvController extends Controller
{
    public function index()
    {
        return view('csv/index',[
            'message' => 'CSVファイルをアップロードしてください',
        ]);
    }

    public function finish(Request $request)
    {

        // ファイルバリデーション
        $file_validation_array = [
            'csv_file' => [
                        'required', // 必須
                        'max:1024', // ファイルサイズ上限は設定値以下か
                        'file', // file属性でアップロードされたファイルか
                        'mimes:csv,txt', // 拡張子がcsvか,
            ],
        ];

        $file_validator = Validator::make($request->all(), $file_validation_array);

        if ($file_validator->fails()) {
            return redirect('/csv/')
                        ->withErrors($file_validator);
        };

        //ファイル名取得
        $csv_name = $request->file('csv_file')->getClientOriginalName();

        // ファイル保存
        $csv_path = $request->file('csv_file')->storeAs('csv_data', $csv_name);

        // CSV情報の取得
        $csv_content = new \SplFileObject(storage_path('app/csv_data/'.$csv_name));
 
        $csv_content->setFlags(
          \SplFileObject::READ_CSV |      // CSVとして行を読み込み
          \SplFileObject::READ_AHEAD |    // 先読み/巻き戻しで読み込み
          \SplFileObject::SKIP_EMPTY |    // 空行を読み飛ばす
          \SplFileObject::DROP_NEW_LINE   // 行末の改行を読み飛ばす
        );        

        // 配列に変換
        $csv_data = [];

        foreach($csv_content as $value) {

            // 文字コード変換
            $value = mb_convert_encoding($value, "UTF-8");

            // 項目行を省く        
            if($value[0] == "名前"){
                continue;
            }

            $csv_data[] = [
                'name' => $value[0],
                'age' => $value[1],
                'email' => $value[2],
            ];
            
        }

        // CSVデータのバリデーション
        $data_validation_array = [
            '*.name' =>['required', 'string'],
            '*.age' =>['required', 'numeric'], 
            '*.email' =>['required', 'string'], 
        ];

        $csv_validator = Validator::make($csv_data, $data_validation_array);

        if ($csv_validator->fails()) {

            // CSVファイルの削除
            Storage::delete('csv_data/'.$csv_name);

            return redirect('/csv/')
                        ->withErrors($csv_validator);
        };

        // 登録処理
        DB::beginTransaction();
        try {

            foreach($csv_data as $value){
                $user_data = DB::table('csv_imports')->insert([
                    'name' => $value['name'],
                    'age' => $value['age'],
                    'email' => $value['email'],
                    'created_at' => date("Y/m/d H:i:s"),
                    'updated_at' => date("Y/m/d H:i:s"),
                ]);
            }

            DB::commit();

            $message = "登録処理が完了しました。";

        } catch (Throwable $e) {

            DB::rollBack();
            $message = "登録処理に失敗しました。";

        }

        // CSVファイルの削除
        Storage::delete('csv_data/'.$csv_name);
        
        return view('/csv/index',[
            'message' => $message,
        ]);
    }

}

SplFileObject

CSVデータを取得するにあたり、PHPに標準で備わっている SplFileObject クラスを使用している。
setFlagsによる4つの設定が1つでも欠けると、データを取得できないので注意。

\app\Http\Controllers\Csv\CsvController.php
// CSV情報の取得
$csv_content = new \SplFileObject(storage_path('app/csv_data/'.$csv_name));

$csv_content->setFlags(
  \SplFileObject::READ_CSV |      // CSVとして行を読み込み
  \SplFileObject::READ_AHEAD |    // 先読み/巻き戻しで読み込み
  \SplFileObject::SKIP_EMPTY |    // 空行を読み飛ばす
  \SplFileObject::DROP_NEW_LINE   // 行末の改行を読み飛ばす
); 

公式: PHP: SplFileObject - Manual

CSVデータのバリデーション

SplFileObject クラスを使用して取得したデータは配列に入れ直している。
その配列は *.設定した項目名 でバリデーションできる。
配列のバリデーションは他の処理でも使えると思うので、覚えておくのをオススメする。

\app\Http\Controllers\Csv\CsvController.php
// CSVデータのバリデーション
$data_validation_array = [
    '*.name' =>['required', 'string'],
    '*.age' =>['required', 'numeric'], 
    '*.email' =>['required', 'string'], 
];

$csv_validator = Validator::make($csv_data, $data_validation_array);

if ($csv_validator->fails()) {

    // CSVファイルの削除
    Storage::delete('csv_data/'.$csv_name);

    return redirect('/csv/')
                ->withErrors($csv_validator);
};

View

\resources\views\search\index(.blade).php
<form method="POST" action="{{ route('csv.finish') }}" enctype="multipart/form-data">
    <input name="csv_file" type="file" accept=".csv,.txt">
        @if($errors->all())
            <p style="color: red;">{{ $errors }}</p>
        @endif
    <p>{{ $message }}</p>
    <button type="submit">アップロードする</button>
    @csrf
</form>

ルーティング処理

今回はviewに使うファイルが1つしかない。
なら、どうやって画面遷移するのか。

その答えは、\routes\web.phpで設定した name()
name()を設定することで、今回のように {{ route(name()で設定した名前) }} で、name()を設定したルーティング処理へ遷移できる。
name()があると便利な場面が多いので、ルーティング処理の際にはできるだけ書いておこう。

ファイルのアップロード

今回のようにファイルをアップロードするフォームを作成する際、<form>タグに enctype="multipart/form-data" 属性をつける必要がある。
これも大切な事なので忘れずに。

CSVファイル

sample.csv
名前,年齢,メールアドレス
t,10,test@exaple.com
e,11,test@exaple.com
s,12,test@exaple.com
t,13,test@exaple.com

参考記事

3
2
1

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