はじめに
こんにちは。カイザーです。今回は、Laravel AdminにCSVインポート機能を追加する方法について、紹介します。
今回は、以下のようなbooksテーブルとBookモデルに対しての、CSVインポート実装を説明します。
なお、booksテーブルのマイグレーションとモデルの作成方法については説明を省略します。
1 2 3 4 5 6 7 | Schema::create('books',function(Blueprint$table){ $table->bigIncrements('id'); $table->string('title'); $table->string('author'); $table->date('published_at'); $table->timestamps(); }); |
完成形はこのようになります。
下準備
Laravel Adminの導入
Laravel Adminを導入しますが、公式ドキュメント通りなので、細かい説明は省略します。
1 2 3 4 5 6 7 8 9 10 11 | # composerを使用してインストール $composer require encore/laravel-admin # 設定ファイルをconfig配下にコピーする。(このファイルを変更することで、インストールディレクトリやDB接続、テーブル名を変更可能となる。) $php artisan vendor:publish--provider="Encore\Admin\AdminServiceProvider" # Laravelプロジェクトに、Laravel Adminを追加し、必要なmigrationが実行される。 $php artisan admin:install # Bookモデルに対して、Laravel Admin用コントローラを生成する $php artisan admin:makeBookController--model=App\\Book |
「app/Admin/routes.php」に対してルーティングを追加するよう指示されるので、指示通りに追記します。
1 2 3 4 5 6 7 8 9 | Route::group([ 'prefix' =>config('admin.route.prefix'), 'namespace' =>config('admin.route.namespace'), 'middleware' =>config('admin.route.middleware'), ],function(Router$router){ $router->get('/','HomeController@index')->name('admin.home'); // 追加したBook用の管理画面コントローラへのルーティングを追記 $router->resource('books',BookController::class); }); |
「http://localhost/admin/books」にアクセスし、ログイン後に管理画面が表示されれば準備完了です。
(初期は、ID・パスワード共にadminです。)
Laravel Excelの導入
Laravel Excelは、ExcelやCSVなどといった、スプレッドシートからDBにインポートしたり、逆にエクスポートしたり出来るライブラリです。
モデルと連携しやすいので、簡単にCSVインポートを実装することができます。
Laravel Adminとは全く別のライブラリなので、単体で使用することも可能です。
1 2 3 4 | # composerでインストール $composer requiremaatwebsite/excel # 設定ファイルをconfig配下にコピーする。 $php artisan vendor:publish--provider="Maatwebsite\Excel\ExcelServiceProvider" |
Laravel Excelの導入はこれで完了です。
Laravel Excelの実装
まず先に、Laravel Excelを使用して、CSVをインポートする機能を実装していきます。
Laravel ExcelではImport系もしくはExport系クラスを作成し、そこにインポートもしくはエクスポートに関する設定を実装していきます。
そして、ExcelクラスのスタティックメソッドにImport/Exportクラスのインスタンスを渡すことで、実際にインポートもしくはエクスポートを行います。
今回はインポート機能を実装するため、まずImportクラスを作成します。
Importクラスの作成
Importクラスは、artisanコマンドから生成できます。
1 | $php artisan make:import BooksImport--model=Book |
生成された、BooksImportクラスを編集します。
初期状態では、 ToModel
が実装されていますが、今回はCSV上のidカラムと、テーブル上のidカラムが一致している場合は更新し、なけければ新規作成する、という形にしたいため、 OnEachRow
とした上で、 public function onRow(Row $row)
を実装しています。ToModel
では、返却したモデルがLaravel Excel側で自動的にsaveされますが、 OnEachRow
では、保存処理は行われません。
そのため、保存に関わる処理を、開発者側でハンドリングすることができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | classBooksImport implementsOnEachRow,WithHeadingRow { /** * @param Row $row */ publicfunctiononRow(Row$row) { $row=$row->toArray(); // booksテーブルのidで、CSVと合致するものがあれば更新し、なければ新規作成する Book::updateOrCreate( [ 'id'=>$row['id'] ], [ 'title' =>$row['title'], 'author' =>$row['author'], 'published_at' =>$row['published_at'], ] ); } } |
EloquentのupdateOrCreateの使い方は、公式ドキュメントを参照してください。
モデルのfillable化
Laravel Excelと直接関係はありませんが、 updateOrCreate()
での複数カラム更新を行うため、モデル側の $fillable
を実装する必要があります。
デフォルトで、ハッシュによる意図しないカラムを更新されないように、複数カラムの更新がデフォルトで不可能となっています。そのため、 $fillable
を実装する場合も、CSVで更新可能とするカラムのみを指定するようにしましょう。
1 2 3 4 5 6 7 8 | classBookextendsModel { protected$fillable=[ 'title', 'author', 'published_at', ]; } |
Laravel Adminの実装
次は、Laravel Admin側の実装です。
Laravel Adminのカスタムツールを使用して、インポートボタンを実装し、Laravel Excelを呼び出して実際にCSVインポートします。
CSVインポートボタンの作成
「app>Admin>Extensions>Tools」に ImportButton.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 | classImportButtonextendsAbstractTool { /** * @var string CSVインポートのURL生成に使用するパス */ private$path; /** * ImportButton constructor. * @param $path */ publicfunction__construct(string$path) { $this->path=$path; } /** * @return string インポートボタンのJavaScript */ privatefunctionscript() { return<<<EOT $('#import-file').on('change', function () { var isConfirmed = confirm("Is this OK?"); if (!isConfirmed) { return; } var formData = new FormData(); formData.append("file", $("#import-file").prop("files")[0]); formData.append("_token", LA.token); // CSVファイルをアップロードする $.ajax({ method: "POST", url: "/admin/$this->path/csv/import", data: formData, processData: false, contentType: false, success: function (response) { console.log(response); $.pjax.reload("#pjax-container"); toastr.success('Upload Successful'); } }) }); EOT; } publicfunctionrender() { // インポートボタンのビュー生成 Admin::script($this->script()); returnview('admin.tools.import_button'); } } |
render()
で、ツールに表示するインポートボタンのViewを返却します。
また、ツールの動作はjQueryでの実装が推奨されているため、 script()
でインポートボタンを押して、CSVファイル選択後にAjaxで送信する処理を実装しています。
この送信先エンドポイントは実装する必要があるため、後で説明します。
render()
で返却するViewの、Bladeファイルも必要です。
「resources>views>admin>tools」に import_button.blade.php
というファイル名で作成します。
1 2 3 4 5 6 | <label id="input"> <span class="btn btn-sm btn-twitter"> <span><iclass="fa fa-upload"></i>Import</span> <input type="file"id="import-file"name="csvfile"style="display:none"> </span> </label> |
作成したCSVインポートボタンをツール表示する
「app>Admin>Controllers」のBookControllerクラスを編集します。
管理画面のCRUDのうち、一覧表示に関する設定は grid()
で行います。
1番初めのartisanコマンドで、自動生成されているので、そこに追記します。
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 | classBookControllerextendsAdminController { ・・・ /** * Make a grid builder. * * @return Grid */ protectedfunctiongrid() { $grid=newGrid(newBook); $grid->column('id',__('Id')); $grid->column('title',__('Title')); $grid->column('author',__('Author')); $grid->column('published_at',__('Published at')); $grid->column('created_at',__('Created at')); $grid->column('updated_at',__('Updated at')); // 作成したImportButtonをツールに表示 $grid->tools(function($tools){ $tools->append(newImportButton('books')); }); return$grid; } ・・・ } |
CSVインポートのエンドポイント作成
最後に、CSVインポートボタンからAjaxでアップロードされるCSVの受け口を作ります。
ここで、アップロードされたCSVを受け取り、Laravel Excelに渡してインポートさせます。
この時、先ほど作成したインポートクラスのインスタンスを使用します。
1 2 3 4 5 6 7 8 9 10 11 | classBookControllerextendsAdminController { ・・・ protectedfunctionimportCsv(Content$content,Request$request) { // アップロードされたCSVファイル $file=$request->file('file'); // インポート Excel::import(newBooksImport(),$file); } } |
このエンドポイントにアクセスするためのルーティングをroutes.php
追加します。
1 2 3 4 5 6 7 8 9 10 | Route::group([ 'prefix' =>config('admin.route.prefix'), 'namespace' =>config('admin.route.namespace'), 'middleware' =>config('admin.route.middleware'), ],function(Router$router){ $router->get('/','HomeController@index')->name('admin.home'); $router->resource('books',BookController::class); // CSVインポート用エンドポイントのルーティング追加 $router->post('books/csv/import','BookController@importCsv'); }); |
これで完了です!
さいごに
管理画面を爆速で構築できるLaravel Adminですが、カスタマイズは意外と大変でした。
ちなみに、CSVエクスポートは何もしなくても、Laravel Admin単体で出来ます。