ブラウザ上でCSVファイルを解析し、SPIRALのデータベースに一括更新するサンプルプログラムを紹介します。
注意点
・ このサンプルは、SPIRALのページで動作することを前提としています。
・ 更新先のDBと、CSVの列構成を事前に一致させておく必要があります。
・ CSVのヘッダー行にはフィールドの識別名を入れる必要があります。
・
・ 更新先のDBと、CSVの列構成を事前に一致させておく必要があります。
・ CSVのヘッダー行にはフィールドの識別名を入れる必要があります。
・
$keyNameには更新キーとなるフィールドの識別名及びヘッダーのカラム名と合致した物を入力してください。
実装の概要
1. ユーザーがファイル選択フォームでCSVファイルを選ぶ。
2. JavaScriptがファイルを読み込み、ヘッダー行とデータ行に分解してJSON文字列に変換する。
3. 変換したJSONを、フォーム内のhiddenフィールドにセットする。
4. ユーザーが「アップロード」ボタンを押すと、フォームが同じページにPOST送信される。
5. サーバーサイドのPHPがPOSTされたJSONデータを受け取る。
6.
を使用して、データをDBに一括更新する。
7. 処理結果のメッセージを画面に表示する。
2. JavaScriptがファイルを読み込み、ヘッダー行とデータ行に分解してJSON文字列に変換する。
3. 変換したJSONを、フォーム内のhiddenフィールドにセットする。
4. ユーザーが「アップロード」ボタンを押すと、フォームが同じページにPOST送信される。
5. サーバーサイドのPHPがPOSTされたJSONデータを受け取る。
6.
$commonBase->apiCurlAction("PATCH", "/apps/" . APP_ID . "/dbs/" . DB_ID . "/records/bulk", $payload); を使用して、データをDBに一括更新する。
7. 処理結果のメッセージを画面に表示する。
HTMLコード (bodyタブ)
まず、ユーザーが操作する画面を作成します。
<!-- 更新結果メッセージの表示 -->
<p th:if="${cp.result.value['result'] != null}" th:text="${cp.result.value['result']}"></p>
<form id="csvForm" method="POST">
<input type="file" id="csvFile" accept=".csv" required>
<input type="hidden" name="columns_json" id="columnsJson">
<input type="hidden" name="data_json" id="dataJson">
<button type="submit">CSVをアップロード</button>
</form>
JavaScriptコード (JavaScriptタブ)
次に、クライアントサイドの動作を担当するJavaScriptです。
CSVファイルを解析し、フォーム送信用のデータを作成します。
document.addEventListener('DOMContentLoaded', function () {
function parseCSV(text) {
const rows = [];
let row = [];
let value = '';
let inQuotes = false;
let i = 0;
while (i < text.length) {
const char = text[i];
const next = text[i + 1];
if (inQuotes) {
if (char === '"' && next === '"') {
value += '"';
i++;
} else if (char === '"') {
inQuotes = false;
} else {
value += char;
}
} else {
if (char === '"') {
inQuotes = true;
} else if (char === ',') {
row.push(value);
value = '';
} else if (char === '\n' || char === '\r') {
if (char === '\r' && next === '\n') i++;
row.push(value);
value = '';
rows.push(row);
row = [];
} else {
value += char;
}
}
i++;
}
if (value !== '' || row.length > 0) {
row.push(value);
rows.push(row);
}
return rows;
}
document.getElementById('csvFile').addEventListener('change', function () {
const file = this.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function (e) {
const csvText = e.target.result;
const parsed = parseCSV(csvText);
if (parsed.length < 2) {
alert('データ行が不足しています');
return;
}
const columns = parsed[0];
const data = parsed.slice(1).filter(row => row.length > 0 && row.some(v => v !== ''));
document.getElementById('columnsJson').value = JSON.stringify(columns);
document.getElementById('dataJson').value = JSON.stringify(data);
};
reader.readAsText(file, 'UTF-8');
});
});
PHPコード (PHPタブ)
最後に、サーバーサイドでAPI連携を行うPHPスクリプトです。
フォームからPOSTされたJSONデータを受け取り、SPIRAL APIを呼び出してDB更新処理を実行します。
<?php
//------------------------------
// 設定値
//------------------------------
define("API_URL", "https://api.spiral-platform.com/v1");
define("API_KEY", "");
define("APP_ROLE", "");
define("APP_ID", "");
define("DB_ID", "");
// ▼ 任意の更新キー名を指定(例:'id' ならレコードID、'email' などフィールド識別名)
$keyName = 'csvId'; // ←必要に応じて変更('_id' としてもOK)
$columns = $SPIRAL->getParams("columns_json");
$data = $SPIRAL->getParams("data_json");
// POST受信処理
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($columns, $data) && $columns && $data) {
if (isset($columns[0]) && is_string($columns[0]) && str_starts_with($columns[0], '[')) {
$columns = json_decode($columns[0], true);
}
if (isset($data[0]) && is_string($data[0]) && str_starts_with($data[0], '[')) {
$data = json_decode($data[0], true);
}
if (!is_array($columns) || !is_array($data)) {
$message = "データ形式が不正です";
$SPIRAL->setTHValue("result", $message);
}
// キー列の位置を特定
// 'id' or '_id' はID列として扱う。それ以外は任意フィールド名として扱う
$isRecordIdKey = ($keyName === 'id' || $keyName === '_id');
// columns に keyName が含まれていないと更新対象を特定できない
$searchKey = $isRecordIdKey ? ($keyName === '_id' ? 'id' : 'id') : $keyName;
$keyIndex = array_search($searchKey, $columns, true);
if ($keyIndex === false) {
$message = "更新キー '{$keyName}' に対応する列が columns に見つかりません";
$SPIRAL->setTHValue("result", $message);
}
$commonBase = CommonBase::getInstance();
$records = [];
foreach ($data as $row) {
if (!is_array($row)) continue;
// まず更新キーを取り出し
if (!array_key_exists($keyIndex, $row) || $row[$keyIndex] === '' || $row[$keyIndex] === null) {
// 更新キー未指定の行はスキップ
continue;
}
$keyValue = $row[$keyIndex];
// レコード本体を生成(列順でマッピング)
$rec = [];
foreach ($columns as $idx => $colName) {
if (!array_key_exists($idx, $row)) continue;
$value = $row[$idx];
// APIではIDは _id フィールド名、任意フィールドはそのまま
if ($isRecordIdKey && $colName === 'id') {
// 値は後で _id に入れるのでスキップ(必要なら通常フィールドとして残しても良いが冗長)
continue;
}
// 更新不可フィールド(password/file/user)はサーバ側でエラーになるため送らない方が安全
$rec[$colName] = $value;
}
// 更新キーの付与
if ($isRecordIdKey) {
$rec['_id'] = $keyValue;
} else {
// 任意の「入力必須かつ重複不可」のテキスト/メールアドレス型フィールド名
$rec['_updateKey'] = [$keyName => $keyValue];
}
$records[] = $rec;
}
if (empty($records)) {
$message = "更新対象がありません({$keyName} の値が空または行が不正)";
$SPIRAL->setTHValue("result", $message);
}
// PATCH /records/bulk による一括更新
$payload = ["records" => $records];
$result = $commonBase->apiCurlAction("PATCH", "/apps/" . APP_ID . "/dbs/" . DB_ID . "/records/bulk", $payload);
// 成功/失敗判定(環境差に配慮)
if (is_array($result)) {
if ((isset($result["ids"]) && is_array($result["ids"]) && count($result["ids"]) > 0)
|| (isset($result["updated"]) && (int)$result["updated"] > 0)
|| (isset($result["count"]) && (int)$result["count"] > 0)) {
$idsText =
isset($result["ids"]) && is_array($result["ids"]) ? ("(ID: " . implode(", ", $result["ids"]) . ")") : "";
$count =
isset($result["updated"]) ? (int)$result["updated"] :
(isset($result["count"]) ? (int)$result["count"] : count($records));
$message = "一括更新に成功しました:{$count}件 {$idsText}";
} else {
$message = "更新エラー:" . json_encode($result, JSON_UNESCAPED_UNICODE);
}
} else {
$message = "更新エラー:API応答の解析に失敗しました";
}
$SPIRAL->setTHValue("result", $message);
} else {
$SPIRAL->setTHValue("result", "");
}
//------------------------------
// 共通モジュール
//------------------------------
class CommonBase {
/**
* シングルトンインスタンス
* @var UserManager
*/
protected static $singleton;
public function __construct() {
if (self::$singleton) {
throw new Exception('must be singleton');
}
self::$singleton = $this;
}
/**
* シングルトンインスタンスを返す
* @return UserManager
*/
public static function getInstance() {
if (!self::$singleton) {
return new CommonBase();
} else {
return self::$singleton;
}
}
/**
* V2用 API送信ロジック
* @return Result
*/
function apiCurlAction($method, $addUrlPass, $data = null, $multiPart = null, $jsonDecode = null) {
$header = array(
"Authorization:Bearer ". API_KEY,
"X-Spiral-Api-Version: 1.1",
);
if($multiPart) {
$header = array_merge($header, array($multiPart));
} else {
$header = array_merge($header, array("Content-Type:application/json"));
}
if(APP_ROLE){
$header = array_merge($header, array("X-Spiral-App-Role: ".APP_ROLE));
}
// curl
$curl = curl_init();
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_URL, API_URL. $addUrlPass);
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
if ($method == "POST") {
if ($multiPart) {
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
} else {
curl_setopt($curl, CURLOPT_POSTFIELDS , json_encode($data));
}
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
}
if ($method == "PATCH") {
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
}
if ($method == "DELETE") {
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
}
$response = curl_exec($curl);
if (curl_errno($curl)) echo curl_error($curl);
curl_close($curl);
if($jsonDecode){
return $response;
}else{
return json_decode($response, true);
}
}
}
?>
実行結果
ページにアクセスすると、ファイル選択フォームが表示されます。
CSVファイルを選択して「アップロード」ボタンをクリックすると、
ページがリロードされ、処理結果(成功またはエラーメッセージ)が画面上部に表示されます。
まとめ
本記事では、JavaScriptでCSVを解析し、PHPでSPIRALのDBに更新する一連の流れを解説しました。
実際に活用される際はCSVファイルの中身に応じて適切なパースロジックを実装する必要があります。
実際に活用される際はCSVファイルの中身に応じて適切なパースロジックを実装する必要があります。