LoginSignup
2
2

More than 3 years have passed since last update.

【Laravel】CSVインポート機能の実装(Ajax)

Last updated at Posted at 2020-12-30

はじめに。

非同期通信を用いてCSVファイルのインポート機能にバリデーションチェックまでを含めた手順を記事にしています

開発環境

  • Laravel 8.0
  • php 7.3
  • goodby/csv: 1.3

UML

UML.png

Goodby/CSVのインストール

Goodby/CSVとはCSVをインポート/エクスポートするためのPHPライブラリ

まずはターミナル・またはコマンドプロンプトでプロジェクト直下に下記コマンドを打ち込んでください。

composer require goodby/csv

上記コマンド後にcomposer.jsonにgoodby/csvが入ってることを確認

composer.json
"goodby/csv": "^1.3",

Model/Viewを作成

まずはモデル作成(オプションに--migrationを付けてマイグレーションファイルも一緒に作成)

php artisan make:model Person --migration

create_people_table.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreatePeopleTable extends Migration
{

    public function up()
    {
        Schema::create('people', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name')->comment('名前');
            $table->string('email')->comment('メールアドレス');
            $table->string('tel',20)->nullable()->comment('電話番号');
            $table->timestamps();
        });
    }


    public function down()
    {
        Schema::dropIfExists('people');
    }
}

view直下にform.blade.phpを作成して内容を下記に書き換えてください

form.blade.php

<h1>CSVインポート演習</h1>

<script type="text/javascript">

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

        var form = new FormData();
        form.append("csv", $("#csv").prop("files")[0]);

        $.ajax({
            type: 'POST',
            url: '/api/csv/apiCsvUpload',
            data: form,
            processData: false,
            contentType: false,
        })
            .done(function (data) {
                console.log(data);
                console.log('成功');
            })
            .fail(function (XMLHttpRequest, textStatus, errorThrown) {
                console.log(errorThrown);
                console.log('失敗');
            });
    };
</script>

<form action="" method="post" enctype="multipart/form-data" id="imgForm">
    {{ csrf_field() }}
    <div class="row">
        <div class="col-11">
            <div class="custom-file">
                <label><input type="file" id="csv" name="file_data" class="custom-file-input">ファイルを選択</label>
                <p>選択されていません</p>
            </div>
        </div>
    </div>
    <button class="btn btn--orange btn--radius" onclick="upload(); return false;">アップロード</button>
</form>

<html>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.3/jquery.min.js"
        integrity="sha384-I6F5OKECLVtK/BL+8iSLDEHowSAfUo76ZL9+kGAgTRdiByINKJaqTPH/QVNS1VDb"
        crossorigin="anonymous"></script>

<script>

    // ファイルを選択すると、コントロール部分にファイル名を表示
    $('.custom-file-input').on('change', function () {
        $(this).next('.custom-file-label').html($(this)[0].files[0].name);
    })

    $('input').on('change', function () {
        var file = $(this).prop('files')[0];
        $('p').text(file.name);
    });
</script>
</body>
</html>

Controller

  • Goodby/CSV の config設定
  • Goodby/CSV ライブラリでCSVデータをパース
  • get_csv_userメソッドでkeyとCSVデータを配列にセット
  • regist_user_csvメソッドでDBに保存

コントローラー作成

php artisan make:controller CsvImportController

CsvImportController
<?php

namespace App\Http\Controllers;

use Goodby\CSV\Import\Standard\Interpreter;
use Goodby\CSV\Import\Standard\Lexer;
use Goodby\CSV\Import\Standard\LexerConfig;
use Illuminate\Http\Request;
use App\Models\Person;
use App\Requests\PersonValidate;
use Illuminate\Http\Response;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;


class CsvImportController extends Controller
{

    public function index()
    {
        return view('/form');
    }

    public function apiCsvUpload(Request $request)
    {
        if ($request->hasFile('csv') && $request->file('csv')->isValid()) {
            $tmpname = uniqid("CSVUP_") . "." . $request->file('csv')->guessExtension(); 
            $request->file('csv')->move(public_path() . "/csv/tmp", $tmpname);
            $tmppath = public_path() . "/csv/tmp/" . $tmpname;

            $config_in = new LexerConfig();
            $config_in
                ->setFromCharset("SJIS-win")
                ->setIgnoreHeaderLine(true) 
            ;
            $lexer_in = new Lexer($config_in);

            $datalist = array();

            $interpreter = new Interpreter();
            $interpreter->addObserver(
                function (array $row) use (&$datalist) {
                    $datalist[] = $row;
                }
            );

            $lexer_in->parse($tmppath, $interpreter);

            unlink($tmppath);

            $valid = new PersonValidate();

            foreach ($datalist as $row) {
                $csv_person = $this->getCsvPerson($row);

                $this->registPersonCsv($csv_person, $valid->rules());
            }
            return response()->json($csv_person);
        }
        return redirect('/form')->with('flashmessage', 'CSVの送信エラーが発生しましたので、送信を中止しました。');
    }


    private function getCsvPerson($row)
    {
        return [

            'name' => $row[0],
            'email' => $row[1],
            'tel' => $row[2],

        ];
    }

    private function registPersonCsv(array $person, array $rules)
    {
        if ($validator = Validator::make($person, $rules)->validate()) {

            $newperson = new Person;
            foreach ($person as $key => $value) {
                $newperson->$key = $value;
            }

            $newperson->save();
        }
    }
}

次はバリデーション作成

PersonValidate.php
<?php

namespace App\Http\Requests;

use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Validator as LocalValidator;
use Illuminate\Validation\ValidationException;

class PersonValidate extends FormRequest
{

    public function rules()
    {
        return [
            'name' => 'nullable|max:30',
            'email' => 'nullable|max:100|email',
            'tel' => 'nullable|Integer',
        ];
    }
}

最後に、web.phpとapi.phpにroute情報を記述

web.php
use App\Http\Controllers\CsvImportController;

Route::get('/form',  [CsvImportController::class, 'index']);
api.php
use App\Http\Controllers\CsvImportController;

Route::namespace('csv')->group(function () {
Route::match(['get', 'post'], '/csv/apiCsvUpload', [CsvImportController::class, 'apiCsvUpload']); 
});

動作確認

42c7eb87def89ee9f0da8ef090316f1e.png

バリデーションも動作していることを確認
49b6123cb8c548f3dc24492a1276c74b.png

以上でLaravelでの非同期csvインポートの手順になります。

従来のフォームリクエストとは違ってControllerの中で配列を作成して、カンマ区切りでデータを挿入していくのは四苦八苦しながらも楽しめました。

引き続き学習を続けた中でのアウトプットを頑張っていきます。
※何か間違いやこうした方がいいよなどご指摘があれば遠慮なくコメントにお寄せいただけると幸いです(^o^)

2
2
0

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