LoginSignup
2

More than 1 year has passed since last update.

posted at

updated at

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

サンプル

入力画面

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

参考記事

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
What you can do with signing up
2