PHPプログラムとigGridの連携でCRUD機能を実装する方法をご紹介します。
以下の内容について実装します。
- PHPを利用してデータベースからグリッド表示用のデータを取得、JSONデータに変換する。ajaxで該当のデータをグリッドにバインドする。
- 行の新規追加、更新、削除に関する変更を保存ボタンのクリックでまとめてPHP に対してポストする。PHP プログラムではポストの値を受け取ってデータベースに保存する
※本記事ではajaxを用いたグリッドとPHP 間のデータ受け渡しの方法説明を目的としており、データベースの処理部分は割愛しております。
igGridの用意
以下のASP.NETフレームワークを利用したサンプルと同じ振る舞いをするigGridを用意しました。行編集設定及び、グリッドに対する変更を一旦ローカルに保持しておき、まとめて更新を反映するという挙動を実装します。
https://jp.igniteui.com/grid/basic-editing
$(function () { getData().done(function(result) { var data = result; var next_id = getAutoIncrement()["responseText"]; var grid = $("#grid").igGrid({ primaryKey: "id", width: '100%', columns: [ { headerText: "ID", key: "id", dataType: "number", width: "15%" }, { headerText: "First Name", key: "first_name", dataType: "string", width: "25%" }, { headerText: "Last Name", key: "last_name", dataType: "string", width: "25%" }, { headerText: "Birthday", key: "birthdate", dataType: "date", width: "35%" } ], autofitLastColumn: false, autoGenerateColumns: false, dataSource: data, updateUrl : 'DataTransaction.php', features: [ { name: 'Updating', generatePrimaryKeyValue: function(evt, ui){ ui.value = next_id; }, rowAdded: function(evt, ui){ next_id++; }, columnSettings: [ { columnKey: "id", readOnly: true, }, { columnKey: "first_name", editorType: 'string', validation: true, }, { columnKey: "last_name", editorType: 'string', validation: true, }, { columnKey: "birthdate", editorType: 'date', validation: true, } ] } ] }); $("#saveChanges").igButton({ labelText: $("#saveChanges").val(), disabled: true }); $("#undo").igButton({ labelText: $("#undo").val(), disabled: true }); $("#redo").igButton({ labelText: $("#redo").val(), disabled: true }); loadingIndicator = new GridModalLoadingIndicator(grid); grid.on("iggriddatabinding", function (e, args) { loadingIndicator.show(); }); grid.on("iggriddatabound", function (e, args) { loadingIndicator.hide(); }); grid.on("iggridupdatingrowdeleted", function (e, args) { $("#undo").igButton("option", "disabled", false); $("#saveChanges").igButton("option", "disabled", false); }); grid.on("iggridupdatingrowadded", function (e, args) { $("#undo").igButton("option", "disabled", false); $("#saveChanges").igButton("option", "disabled", false); }); grid.on("iggridupdatingeditrowended", function (e, args) { if (args.update) { $("#undo").igButton("option", "disabled", false); $("#saveChanges").igButton("option", "disabled", false); } }); $("#undo").on('igbuttonclick', function (e, args) { updates = $.extend({}, grid.data('igGrid').pendingTransactions()); $.each(updates, function (index, transaction) { grid.igGrid("rollback", transaction.rowId, true); }); $("#redo").igButton("option", "disabled", false); $("#undo").igButton("disable"); $("#saveChanges").igButton("disable"); return false; } ); $("#redo").on('igbuttonclick', function (e) { $.each(updates, function (index, transaction) { switch (transaction.type) { case "row": grid.igGridUpdating('updateRow', transaction.rowId, transaction.row, null, false); break; case "newrow": grid.igGridUpdating('addRow', transaction.row, false); break; case "deleterow": grid.igGridUpdating('deleteRow', transaction.rowId, false); break; } }); $(this).igButton("disable"); $("#undo").igButton("option", "disabled", false); $("#saveChanges").igButton("option", "disabled", false); } ); $("#saveChanges").on('igbuttonclick', function (e) { loadingIndicator.show(); try { grid.igGrid("saveChanges", function (data) { console.log(data); loadingIndicator.hide(); $("#undo").igButton("disable"); $("#saveChanges").igButton("disable"); }, function (jqXHR, textStatus, errorThrown) { alert(JSON.stringify(jqXHR)); alert(errorThrown); alert(JSON.stringify(textStatus)); }); } catch (e) { alert("error"); } } ); }).fail(function(result) { console.log(result); }); });
ajaxでPHP にアクセスし、データベースの情報を取得、グリッドにバインドする。
以下のようにgetData()関数を作成しました。
function getData() { return $.ajax({ type : "GET", url : "DataTransaction.php", dataType:'json', success: function(data, status, xhr){ //成功時の処理 return data; }, error: function(XMLHttpRequest, status, errorThrown){ //失敗時の処理 console.log("fail:" + status); } }); }
PHP 側では以下のようにデータベースの情報を取得し、JSONに変換して返しています。また、PHPでJSONを返す際はヘッダーの設定をします。
//ヘッダーの設定 header("Content-Type: application/json; charset=utf-8"); $sql = "SELECT * FROM authors"; if ($result = $mysqli->query($sql)) { $data = []; while ($row = $result->fetch_assoc()) { array_push($data,$row); } $result->close(); } $mysqli->close(); //JSONへ変換して返す echo json_encode($data); exit;
グリッドの変更内容をデータベースに反映する
igGridの変更内容をデータベースに反映するためにupdateUrl プロパティにPHPファイルを指定します。保存ボタンを押した際に、指定したURLに変更情報がPOSTされます。
updateUrl : 'DataTransaction.php',
POSTを受け取ったPHP側では、受け取ったJSONデータを配列に変換し、ループ内でデータのタイプ(newrow,deleterow,row)によってデータベースに対して、新規追加や削除、更新の処理を行います。
また、igGridに処理の終了を知らせるために json_encode(array(“Success”=>true)) を返します。
header("Content-Type: application/json; charset=utf-8"); $post_data = $_POST["ig_transactions"]; $post_data_array = json_decode($post_data, true); $sql = ""; foreach ( $post_data_array as $row_data ) { if ($row_data["type"] == "newrow") { $id = $row_data["rowId"]; $first_name = $row_data["row"]["first_name"]; $last_name = $row_data["row"]["last_name"]; $birthdate = $row_data["row"]["birthdate"]; $sql .= "INSERT INTO `authors`(`id`, `first_name`, `last_name`, `birthdate`) VALUES ($id,'$first_name','$last_name','$birthdate');"; } if ($row_data["type"] == "deleterow") { $id = $row_data["rowId"]; $sql .= "DELETE FROM `authors` WHERE `id` = $id;"; } if ($row_data["type"] == "row") { $id = $row_data["rowId"]; $first_name = $row_data["row"]["first_name"]; $last_name = $row_data["row"]["last_name"]; $birthdate = $row_data["row"]["birthdate"]; $sql .= "UPDATE `authors` SET `id` = $id,`first_name` = '$first_name',`last_name` = '$last_name',`birthdate` = '$birthdate' WHERE `id` = $id; "; } } $result = $mysqli->multi_query($sql); $mysqli->close(); echo json_encode(array("Success"=>true)); exit;
PrimaryKey の値の Auto Increment 対応
新規行を追加する際に注意する点がひとつあります。データベースのテーブルで、idを主キーとして用いており、Auto Incrementで新規レコード追加の際に新しいidが付与されるような仕様が多いかと思います。一方でグリッド側ではあくまでグリッド側で保持している情報をもとに次のPrimaryKeyの値を求めるため、この差異を調整する必要があります。今回の例では、最初のグリッド描画の際にmysqlから次のIDの数値を取得しておき、rowAdded イベントが発生するたびにIDの値を一つ増やすという形で対応しています。また、igGridで新規行追加の際に付与されるIDは、generatePrimaryKeyValueイベントの返り値 ui の ui.valueを変更することで任意の値にすることができます。
getData().done(function(result) { var data = result; var next_id = getAutoIncrement()["responseText"]; var grid = $("#grid").igGrid({ primaryKey: "id", width: '100%', columns: [ { headerText: "ID", key: "id", dataType: "number", width: "15%" }, { headerText: "First Name", key: "first_name", dataType: "string", width: "25%" }, { headerText: "Last Name", key: "last_name", dataType: "string", width: "25%" }, { headerText: "Birthday", key: "birthdate", dataType: "date", width: "35%" } ], autofitLastColumn: false, autoGenerateColumns: false, dataSource: data, updateUrl : 'DataTransaction.php', features: [ { name: 'Updating', generatePrimaryKeyValue: function(evt, ui){ ui.value = next_id; }, rowAdded: function(evt, ui){ next_id++; }, columnSettings: [ { columnKey: "id", readOnly: true, }, { columnKey: "first_name", editorType: 'string', validation: true, }, { columnKey: "last_name", editorType: 'string', validation: true, }, { columnKey: "birthdate", editorType: 'date', validation: true, } ] } ] }); }).fail(function(result) { console.log(result); });
実行例
以下のようなテーブルを用意しました。
これをサンプルで実行すると以下のようにグリッドの表示が出来ました。
次にグリッド上で新規追加、更新、削除を行ってみます。
変更はまとめてデータベースにコミットします。Saveボタンを押して変更をコミットします。
グリッド及びデータベースに変更が反映されました。
以上のところです。今回セキュリティに関しては言及していませんが、ajax通信の際のCSRF対策や、mysqlにクエリを発行する前のインジェクション対策なども必要となってきますのでご注意ください。
今回作成したサンプルソースは以下よりダウンロードいただけます。参考になれば幸いです。