:::: MENU ::::

microCMSのコンテンツをSpreadsheetから一括更新する

microCMSでこんなことができた!あなたのユースケースを大募集 by microCMS - Qiita Advent Calendar 2024 シリーズ 1の12月10日の記事になります。

microCMSは個人でも会社でも便利に使わせていただいてます。CMSに慣れていない方でも使いやすい管理画面でとても重宝してます。

今回は前から作ってみたかった商品データの更新管理フローを作ってみようと思います。

利用シーン

  • 商品データはSpreadsheetで管理されている
    • microCMSで管理していないのは一覧性と一括編集性の問題
  • 商品データをmicroCMSに投入するのはSpreadsheet利用者
  • ワンアクションですべてのデータ更新を行いたい
  • データ更新前にレビューを挟みたい

構成案

  • 案1:GASでmicroCMSへの同期ボタンを作る
    • アップロード処理に関しては一番シンプル
    • 差分確認はSpreadsheetの履歴から確認可能
    • レビューはSpreadsheet上のコメントか別途Slackでやる形になるのが課題
  • 案2:商品データをGitHubで管理する
    • 一番やりたいことが全部できる
    • PRまでの処理をどうするか、レビュワーにGitHubアカウントが必要となるのが課題

今回はわかりやすさ重視で案1を作ってみます。

データ構成

Spreadsheet

contentIdがあるものは既存、ないものは新規

contentId	cid	name	plan	description	age00	age10	age20	age30	age40	age50	age60	delete	lastSendDate
rpebessc3	c001	商品A	テキスト111	商品説明1	100	150	200	300	350	400	500	FALSE	2024/12/08 15:04:57
32xl7n54i	c002	商品B	テキスト222	商品説明2	500	1000	1500	1800	2000	1500	1000	FALSE	2024/12/08 15:04:58
2ki4avfew2	c003	商品B	テキスト3	商品説明3	500	1000	1500	1800	2000	1500	1000	FALSE	2024/12/08 15:05:00
abs5w_m8hff	c004	商品A	テキスト444	商品説明4	100	200	300	400	500	500	500	TRUE	2024/12/08 15:00:52
	c004	商品A	テキスト444 update	商品説明4	100	150	300	400	500	500	500	FALSE	

microCMS

{
    "apiFields": [
        {
            "idValue": "-nAEAoE_hx",
            "fieldId": "cid",
            "name": "商品ID",
            "kind": "text"
        },
        {
            "fieldId": "name",
            "name": "商品名",
            "kind": "text"
        },
        {
            "fieldId": "plan",
            "name": "契約プラン",
            "kind": "text"
        },
        {
            "fieldId": "description",
            "name": "説明",
            "kind": "textArea"
        },
        {
            "fieldId": "age00",
            "name": "10歳未満",
            "kind": "number"
        },
        {
            "fieldId": "age10",
            "name": "10代",
            "kind": "number"
        },
        {
            "fieldId": "age20",
            "name": "20代",
            "kind": "number"
        },
        {
            "fieldId": "age30",
            "name": "30代",
            "kind": "number"
        },
        {
            "fieldId": "age40",
            "name": "40代",
            "kind": "number"
        },
        {
            "fieldId": "age50",
            "name": "50代",
            "kind": "number"
        },
        {
            "fieldId": "age60",
            "name": "60代以上",
            "kind": "number"
        }
    ],
    "customFields": []
}

GASで同期ボタンを作る

function postRowData(row) {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  const data = sheet.getRange(row, 1, 1, sheet.getLastColumn()).getValues()[0]; // 対象行のデータ取得
  
  const [contentId, cid, name, plan, description, age00, age10, age20, age30, age40, age50, age60, del] = data;

  const apiUrl = "https://{YOUR_SERVICE_ID}.microcms.io/api/v1/{YOUR_API_ENDPOINT}/" + contentId; // API のエンドポイント
  const payload = {
    "cid": cid,
    "name": name,
    "plan": plan,
    "description": description,
    "age00": age00,
    "age10": age10,
    "age20": age20,
    "age30": age30,
    "age40": age40,
    "age50": age50,
    "age60": age60
  };

  const method = del === true ? "DELETE" : (contentId ? "PATCH" : "POST");

  const options = {
    method: method,
    contentType: "application/json",
    payload: method !== "DELETE" ? JSON.stringify(payload) : null,
    headers: {
      "X-MICROCMS-API-KEY": "YOUR_API_KEY"
    }
  };

  try {
    const response = UrlFetchApp.fetch(apiUrl, options);
    const responseData = JSON.parse(response.getContentText());
    Logger.log("Response: " + response.getContentText());

    const lastSendDate = new Date();
    sheet.getRange(row, sheet.getLastColumn()).setValue(lastSendDate); // 実行時刻を入力

    if (!contentId && responseData.id) {
      sheet.getRange(row, 1).setValue(responseData.id); // contentIdがない場合はレスポンスのidを入力
    }
  } catch (e) {
    Logger.log("Error: " + e.message);
  }
}

function onButtonClick() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  const lastRow = sheet.getLastRow();
  for (let row = 2; row <= lastRow; row++) { // 見出し行を除く
    postRowData(row);
  }
}

function onOpen() {
  const ui = SpreadsheetApp.getUi();
  ui.createMenu("Custom Menu")
    .addItem("Send All Rows", "onButtonClick")
    .addToUi();
}

GASにmicroCMSへデータを送る処理とSpreadsheet側にメニューボタンを作る処理を記載。

これだけでSpreadsheetからmicroCMSへの一方向一括同期ができます。作ってみると結構簡単ですね。

注意点としては即本番公開になってしまう点。

POST/PUTリクエストで下書き状態のコンテンツを作成できるようになりました | microCMSブログ

有償プラン限定となりますが、下書き状態でのコンテンツ作成もできますので、必要に応じてdraft列を作って下書き更新にすることもできます。
下書き保存にする場合は、変更のない行はリクエストを送らないようにする処理も入れたほうがいいでしょう。

  if (draft) {
    apiUrl += "?status=draft"; // 下書き作成のためのパラメータを追加
  }

  // 記事削除の場合は DELETE、公開記事の下書き追加の場合は PUT、公開記事の更新の場合は PATCH、新規記事の場合は POST
  const method = del === true ? "DELETE" : (contentId ? (draft ? "PUT" : "PATCH") : "POST"); 

microCMSへのFeatueリクエスト

今回の方法だとGASとはいえプログラムの出番が出てきてしまっているので、完全に設定のみで一括更新とレビューが行えるのが理想ではあります。
できればGitHubみたいなレビューフローがmicroCMSのみで実現できると嬉しいですね。

  • csvをアップロードして一括データ更新
  • レビューに複数コンテンツを含める
    • コンテンツごとにレビューを出さないといけないため、複数コンテンツを1つのレビューで行いたい
    • 上記の一括更新で下書き保存 → 一括レビューができると最高
  • APIの拡張
    • レビュー作成API
    • レビューへのコメントAPI
      • Webhookで実行されたテスト結果をコメントに自動で入れたい