【Laravel】DBデータをCSVでインポートする機能をつくる

さてさて、前回・前々回とDBのユーザーデータに関連する話題をお届けしました。

そして、この流れで何か記事にできないかと考えていたのですが、ひとつまだこのブログでは紹介していない内容を思いつきました。

それは、

CSVファイルのインポート

です。

例えば、CSVファイルにたくさんのユーザー情報が入っていて、このファイルをアップロードすることでDBのusersテーブルにデータを追加することができるという、ある種ユーザービリティにも通じる機能です。

もちろんこの機能を応用すれば、ユーザー情報だけでなく商品データなどその他のデータをインポートすることもできるようになります。

ぜひ皆さんのお役に立てると嬉しいです😊✨

開発環境: Laravel 6.x

前提として

今回はusersテーブルにデータをインポートをするので、Laravelのログイン機能が有効になっている必要があります。詳しくは以下のページをご覧ください。

パッケージをインストールする

CSVのデータをチェックするパッケージcsv-validatorをインストールします。(私が公開しているパッケージです。今回のためにフルで書き直しました😊)

composer require sukohi/csv-validator:3.*

コントローラーをつくる

続いてコントローラーをつくります。
以下のコマンドを実行してください。

php artisan make:controller CsvImportController

すると、app/Http/Controllers/CsvImportController.phpというファイルが作成されるので中身を次のようにしてください。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Sukohi\CsvValidator\Rules\Csv;

class CsvImportController extends Controller
{
    public function create() {

        return view('csv_import.create');

    }

    public function ajax_store(Request $request) {

        $csv_rules = [
            0 => 'required|email|unique:users,email',
            1 => 'required|string',
            2 => 'required|string|min:6'
        ];
        $request->validate([
            'csv_file' => [
                'required',
                'file',
                new Csv($csv_rules, 'sjis-win')
            ]
        ]);

        $csv_data = $request->csv_file_data; // パッケージが作成したCSVデータ

        foreach($csv_data as $row_data) {

            $user = new \App\User();
            $user->email = $row_data[0];
            $user->name = $row_data[1];
            $user->password = bcrypt($row_data[2]);
            $user->save();

        }

        return ['result' => true];

    }
}

この中では、create()がブラウザでアクセスするメソッド、ajax_store()Ajaxを通してアクセスするメソッドになります。

なお、ajax_store()の中では、まず先ほどインストールしたパッケージをつかってバリデーションを実行し、問題がなければアップロードされたCSVのデータを使ってユーザーを新規登録しています。

ビューをつくる

resources/views/csv_import/create.blade.phpというファイルをつくって中身を次のようにします。

<html>
<body>
    <div id="app">
        <h1>CSVファイルでDBにインポート</h1>
        <label>CSVファイル</label><br>
        <input id="file" type="file" @change="onFileChange">
        <div v-if="csvErrors">
            <ul>
                <li v-for="error in csvErrors" v-text="error"></li>
            </ul>
        </div>
        <br>
        <button type="button" @click="onSubmit">インポートする</button>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.0"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.0/axios.min.js"></script>
    <script>

        new Vue({
            el: '#app',
            data: {
                csvFile: null,
                csvErrors: []
            },
            methods: {
                onFileChange(e) {

                    this.csvFile = e.target.files[0];

                },
                onSubmit() {

                    this.csvErrors = [];

                    const url = '/ajax/csv_import';
                    let formData = new FormData();
                    formData.append('csv_file', this.csvFile);
                    axios.post(url, formData)
                        .then(response => {

                            if(response.data.result) {

                                document.getElementById('file').value = '';
                                alert('インポートが完了しました。');

                            }

                        })
                        .catch(error => {

                            this.csvErrors = error.response.data.errors.csv_file;

                        });

                }
            }
        });

    </script>
</body>
</html>

この中で重要なのは2ヶ所です。

まずひとつめは、<input type="file">につけられたchangeイベントです。このイベントが実行されると、onFileChangeが呼ばれ、選択されたファイルの情報がVuedataに保持されることになります。

そして、もうひとつは「インポートする」ボタンをクリックしたときに実行されるonSubmit()の部分です。

ここでは、選択されたcsvファイルをFormDataで送信しています。アクセスするのは先ほどつくったコントローラーのajax_store()です。

なお、もしエラーが発生した場合は<input type="file">の下にv-forを使って表示するようになっています。

実際にブラウザでアクセスするとこうなります。

テストしてみる

では、実際に以下のCSVを送信してテストしてみます。
文字コードはShift_JISです。

ファイルを選択して送信します。

実行後のusersテーブルです。

うまく追加されました😊

では次に、バリデーションがうまく働いているかをチェックするために、今の状態のままで次のCSVファイルを送信してみましょう。

送信すると次のようになりました。

成功です😊✨

ダウンロードする

今回開発したソースコード一式を以下からダウンロードすることができます。

※ ただし、パッケージのインストールはご自身で行ってください。また、storage/app/csvにはアップロード用のuser.csvが入っています。

DBデータをCSVでインポートする機能をつくる

おわりに

ということで、今回はCSVファイルを使ったインポート機能をつくってみました。

比較的にデータをCSVでダウンロードさせる機能は簡単なのですが、アップロードの場合はバリデーションが必要になってくるのでちょっと手間だったりしますが、今回のパッケージを使っていただくと時短で開発ができると思います。

また、今回のパッケージは内部的には同じく私が公開しているfluent-csvというパッケージを使っています。これは元々php-spreadsheetShift_JISCSVが作れなかったので作成したパッケージですが、現在はCSVの「読み・書き」両方に対応しているのでこちらも興味があるかたはぜひ使ってみてくださいね。

ではでは〜!

この記事が役立ちましたらシェアお願いします😊✨