kintoneのテーブルでアプリのレコードの登録・更新・削除をする（３）

今回は「英単語一覧アプリ」から単語を登録・編集・削除したときに「登録用アプリ」側もレコードの登録・編集・削除をする。
というカスタマイズをしてみます。

↓こんなことをやりたいよ。という記事です。
<iframe width="480" height="360" src="https://www.youtube.com/embed/gGAH1Z2Lg4g" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>


↓関連記事はこちら↓。

* [kintoneのテーブルのレコードを別アプリのレコードに登録する](https://qiita.com/juri_don/items/eded7d5d8ce9ef2a3e8a)
* [kintoneのテーブルでアプリのレコードの登録・更新・削除をする（１）](https://qiita.com/juri_don/items/d69e375498e96847d4d9)
* [kintoneのテーブルでアプリのレコードの登録・更新・削除をする（２）](https://qiita.com/juri_don/private/2ddb06c5ddaade5b7209)
* [kintoneのテーブルでアプリのレコードの登録・更新・削除をする（３）（今見てる記事）](https://qiita.com/juri_don/private/70fa08591ac1304df36d)
* [kintoneのテーブルでアプリのレコードの登録・更新・削除をする（４）](https://qiita.com/juri_don/items/aacd9c761ee84f7b7282)


# JavaScript

kintone REST API Client を使います。

https://developer.cybozu.io/hc/ja/articles/900000767263-kintone-JavaScript-Client-kintone-rest-api-client-

## レコード新規追加

英単語一覧アプリのレコードを直接新規追加したときは、登録用アプリに新規レコードを追加します。

```js
// レコード新規追加保存前イベント
kintone.events.on(["app.record.create.submit"], async (event) => {
  const client = new KintoneRestAPIClient();
  const appId = 登録用アプリのID;

  // 新しいレコードのUIDを作成
  const resGetUID = await client.record.getRecords({
    app: kintone.app.getId(),
    fields: "UID",
    query: `order by UID desc`,
  });

  event.record.UID.value =
    resGetUID.records.length > 0 ? Number(resGetUID.records[0].UID.value) + 1 : 1;

  // 登録用アプリのキーTUID も新しく作成
  const resGetTUID = await client.record.getRecords({
    app: appId,
    fields: "TUID",
    query: `order by TUID desc`,
  });

  event.record.TUID.value =
    resGetTUID.records.length > 0 ? Number(resGetTUID.records[0].TUID.value) + 1 : 1;

  // 登録用アプリにレコードを追加
  const rec = {
    テーブル: {
      value: [
        {
          value: {
            英単語: { value: event.record.英単語.value },
            日本語: { value: event.record.日本語.value },
            UID: { value: event.record.UID.value },
          },
        },
      ],
    },
    TUID: {
      value: event.record.TUID.value,
    },
    uids: {
      value: event.record.UID.value,
    },
  };
  const resAdd = await client.record.addRecord({
    app: appId,
    record: rec,
  });

  return event;
});
```

## レコード編集

英単語一覧アプリで直接変更を加えた時は、登録用アプリのテーブルの同じUIDの行を上書きします。

```js
// 編集
kintone.events.on(
  ["app.record.edit.submit", "app.record.index.edit.submit"],
  async (event) => {
    const client = new KintoneRestAPIClient();
    const appId = 登録用アプリのID;
    // 登録用アプリの同じTUIDのレコードを検索
    const resGet = await client.record.getRecords({
      app: appId,
      query: `TUID = "${event.record.TUID.value}"`,
    });

    // 同じUIDの行の日本語と英単語を書き換える
    resGet.records[0].テーブル.value.forEach((r) => {
      if (r.value.UID.value === event.record.UID.value) {
        r.value.日本語.value = event.record.日本語.value;
        r.value.英単語.value = event.record.英単語.value;
      }
    });

    // typeプロパティを更新パラメータのレコード内に書いてるとエラーになるので削除しておく
    const recTableValue = resGet.records[0].テーブル.value.map((r) => {
      delete r.value.UID.type;
      delete r.value.英単語.type;
      delete r.value.日本語.type;
      return r;
    });

    // 登録用アプリのレコードを上書き保存
    const rec = {
      テーブル: {
        value: recTableValue,
      },
    };
    const resUpd = await client.record.upsertRecord({
      app: appId,
      updateKey: {
        field: "TUID",
        value: event.record.TUID.value,
      },
      record: rec,
    });

    return event;
  }
);
```

## レコード削除
登録用アプリのテーブルが1行だけだったらレコードごと削除。
テーブルに複数行ある場合は、該当の行のみ削除して上書き。

```js
kintone.events.on(
  ["app.record.detail.delete.submit", "app.record.index.delete.submit"],
  async (event) => {
    const client = new KintoneRestAPIClient();
    const appId = 登録用アプリのID;

    // 登録用アプリ検索
    const resGet = await client.record.getRecords({
      app: appId,
      query: `TUID = "${event.record.TUID.value}"`,
    });

    // レコードがなかったらそのままレコード削除して終了
    if (resGet.records.length < 1) {
      return event;
    }

    if (resGet.records[0].テーブル.value.length > 1) {
      // テーブル内の同じUIDの行以外を残す
      resGet.records[0].テーブル.value = resGet.records[0].テーブル.value.filter(
        (v) => Number(v.value.UID.value) !== Number(event.record.UID.value)
      );

      // typeプロパティは更新に邪魔なので削除しておく
      const recTableValue = resGet.records[0].テーブル.value.map((r) => {
        delete r.value.UID.type;
        delete r.value.英単語.type;
        delete r.value.日本語.type;
        return r;
      });

      // 残す行のUIDでuidsの内容を作成
      const uids = resGet.records[0].uids.value
        .split(",")
        .filter((v) => Number(v) !== Number(event.record.UID.value));

      // 登録用アプリのレコード上書き
      const rec = {
        テーブル: {
          value: recTableValue,
        },
        uids: { value: uids.join(",") },
      };

      try {
        const resU = await client.record.updateRecord({
          app: appId,
          id: resGet.records[0].$id.value,
          record: rec,
        });
      } catch (e) {
        console.log(e);
        return false;
      }
    } else {
      // テーブルの行が１行だったらレコードごと削除する
      const resD = await client.record.deleteRecords({
        app: appId,
        ids: [resGet.records[0].$id.value],
      });
    }

    return event;
  }
);
```

# まとめ
登録される側のレコードと、登録する側のレコードとの辻褄合わせはなかなか「沼」です。
不整合が起きようとも、辻褄合わせなんて諦めて運用でなんとかする！という方法もアリだと思います。