こんばんは。
一月に入り一気に寒くなってきましたね。
僕の住んでいる場所は山間にあるため、朝方は超冷えます。そして霧が深いので道路&フロントがガチガチに凍ります。
いいところもいっぱいあるのですが、冬の田舎は苦手ですねぇ…
前置きはさておき、今日はLarave-adminでCSVファイルでのテーブルインポートを行ってみたいと思います。
test_usersテーブルの一覧画面は【Laravel-admin】Laravel-adminで新規テーブルを作成しCRUD画面を追加する方法で作成済みです。
Goodby/CSVのインストール
LaravelでCSVからテーブルにデータをインポートすることができるライブラリはいくつかありますが、今回はメモリ効率が非常にいいという「goodby/csv」を使用したいと思います。
参考:goodby/csv
composerを使用してインストールを行います。
1 2 3 |
"require": { "goodby/csv": "*" } |
上記の記述を行ったら「composer install」あるいは「composer update」を行います。
updateを行うと他のライブラリなど、新しいバージョンが存在する場合最新版を取得してしまうので、
その場合は「composer.lock」を一度削除してインストールを行います。
1 |
composer install |
実装内容
一覧画面にインポートボタンを追加し、ボタン押下でCSVのデータをテーブルに挿入できるようにしたいと思います。
※一覧に表示されているデータはファクトリーで作成したダミーデータです。
インポート用のテーブル作成
インポート用にitemsテーブルを作成します。
マイグレーションファイル
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateItemTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('items', function (Blueprint $table) { $table->increments('id'); $table->string('item_name'); $table->integer('quantity'); $table->string('price'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('items'); } } |
Toolクラスを作成
インポート機能を提供するボタンを一覧画面に追加するためにToolクラスを作成します。
一覧画面のカスタムツールの作成に関しては以下のドキュメントを参考にしました。
ajaxSetupでheader情報にcsrfトークンを追加しないとデータをpost出来ないので忘れず追加しましょう。
参考:Custom tools
app/Admin/Extemsion/Tools/CsvImport.phpを以下の内容で作成します。
CsvImport.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
<?php namespace App\Admin\Extensions\Tools; use Encore\Admin\Admin; use Encore\Admin\Grid; use Encore\Admin\Grid\Tools\AbstractTool; use Illuminate\Support\Facades\Request; class CsvImport extends AbstractTool { /** * Set up script for import button. */ protected function script() { return <<< SCRIPT // ボタン押下でCSVインポート $('.csv-import').click(function() { var select = document.getElementById('files'); document.getElementById("files").click(); select.addEventListener('change',function() { var fd = new FormData(); fd.append( "file", $("input[name='hoge']").prop("files")[0] ); $.ajaxSetup({ headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') } }); $.ajax({ type : "POST", url : "/admin/import", data : fd, processData : false, contentType : false, }); }); }); SCRIPT; } /** * Render Import button. * * @return string */ public function render() { Admin::script($this->script()); return view('csv_upload'); } } |
resource/views配下にビューを作成し、ボタン用のHTMLを記述します。
csv_upload.blade.php
1 2 3 4 5 |
<meta name="csrf-token" content="{{ csrf_token() }}"> <div class="btn-group pull-right" style="margin-right: 10px"> <a href="#" class="btn btn-sm btn-twitter csv-import"><i class="fa fa-upload"></i><span class="hidden-xs"> CSVインポート</span></a> <input type="file" id="files" name="hoge" style="display: none"> </div> |
Controller
コントローラのgrid内で、作成したToolクラスを読み込みます。
csvImport関数内でgoodby/csvを使用しCSVファイルをパースし、配列に代入後テーブルにインサートしています。
「$interpreter->unstrict();」を記述するすることで、CSVファイルの列の一貫性がない場合も無視して実行します。
CSVファイルの列の一貫性が必要な場合はコメントアウト等すれば、列の一貫性がない場合エラーとなります。
また、LexerConfigでCSVファイルの区切り文字や囲い文字、文字コードなどを指定することができます。
TestUserCpntroller.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
<?php namespace App\Admin\Controllers; use App\Admin\Models\TestUser; use App\Http\Controllers\Controller; use Encore\Admin\Controllers\HasResourceActions; use Encore\Admin\Form; use Encore\Admin\Grid; use Encore\Admin\Layout\Content; use Encore\Admin\Show; use App\Admin\Extensions\Tools\CsvImport; use Goodby\CSV\Import\Standard\Lexer; use Goodby\CSV\Import\Standard\Interpreter; use Goodby\CSV\Import\Standard\LexerConfig; use Illuminate\Http\Request; use App\Admin\Models\Item; class TestUserController extends Controller { ~中略~ /** * Make a grid builder. * * @return Grid */ protected function grid() { $grid = new Grid(new TestUser); $grid->id('Id'); $grid->name('Name'); $grid->age('Age'); $grid->email('Email'); $grid->created_at('Created at'); $grid->updated_at('Updated at'); $grid->tools(function ($tools) { $tools->append(new CsvImport()); }); return $grid; } ~中略~ public function csvImport(Request $request) { $file = $request->file('file'); $config = new LexerConfig(); $lexer = new Lexer($config); $interpreter = new Interpreter(); $rows = array(); // 行の一貫性は無視 $interpreter->unstrict(); $interpreter->addObserver(function (array $row) use (&$rows) { $rows[] = $row; }); // CSVデータをパース $lexer->parse($file, $interpreter); $data = array(); // CSVのデータを配列化 foreach ($rows as $key => $value) { $arr = array(); foreach ($value as $k => $v) { switch ($k) { case 0: $arr['item_name'] = $v; break; case 1: $arr['quantity'] = $v; break; case 2: $arr['price'] = $v; break; default: break; } } $data[] = $arr; } Item::insert($data); return response()->json( [ 'data' => '成功' ], 200, [], JSON_UNESCAPED_UNICODE ); } } |
まとめ
今回、一覧画面のボタン追加にToolクラスを使用しました。
このToolクラスですが、コントローラで読み込むだけで、表示画面のHTMLにJavascriptを使用して様々な処理を実行できるので使い道はたくさんありそうです!
課題として、CSVのデータはとりあえずはインサートできるようになりましたが、まだまだ改善点は多く、成功時・失敗時のメッセージ表示など調べ切れておりません…
Javascriptを学習しながらUI的に見栄えのいい表示方法が見つかればまた記事にしたいと思います!