N:N(複数対複数)の関連レコード一覧を自作する

目次

はじめに

関連レコード一覧フィールドは関連性が1:Nの場合は標準機能で設定できますが、テーブルの内容を条件とするような、関連性がN:Nの場合は標準機能で設定できません。

こちらのQ&Aの回答 のように詳細データのアプリを別個作成して対応するなどの方法もありますが、今回は、JavaScript APIを利用して関連レコード一覧のテーブルを作成、表示するカスタマイズ方法を説明したいと思います。

デモ環境

デモ環境で実際に動作を確認できます。
https://dev-demo.cybozu.com/k/295/ (External link)

ログイン情報は cybozu developer networkデモ環境 で確認してください。

kintoneアプリの作成

サンプルのアプリとして、学習塾クラスの管理アプリを作成します。

生徒管理アプリとクラス管理アプリを作成して、生徒管理アプリから複数のクラスをルックアップ選択し、クラス管理アプリでは、そのクラスに登録した生徒を一覧表示するように設定します。
1人の生徒が複数のクラスを選択でき、ひとつのクラスには複数の生徒が登録されるので、N:Nの関連性となります。

クラス管理アプリの作成

次の画像とテーブルを参考にクラス管理アプリを作成します。

フィールドの種類 フィールド名 フィールドコード 備考
レコード番号 クラス番号 class_no
文字列(1行) クラスコード class_code
文字列(1行) クラス名 class_name
スペース student_list テーブル表示スペース

生徒管理アプリの作成

次の画像とテーブルを参考に生徒管理アプリを作成します。

フィールドの種類 フィールド名 フィールドコード 備考
レコード番号 生徒番号 student_no
文字列(1行) 生徒コード student_code
文字列(1行) 氏名 student_name
ルックアップ クラスコード class_code テーブル
文字列(1行) クラス名 class_name テーブル

サンプルコード

作成した「クラス管理アプリ」に次のソースコードを参考にしたJavaScriptファイルを作成し、適用します。 テーブルのHTMLのクラス名は、kintoneのスタイルに調和するよう、 こちら のプラグイン用スタイルシートを参考にしています。
こちらの GitHub上のスタイルシート (External link) をダウンロードし、アプリに反映してください。

 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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/*
* N:N(複数対複数)の関連レコード一覧を自作する
* Copyright (c) 2022 Cybozu
*
* Licensed under the MIT License
*/
(() => {
  'use strict';
  kintone.events.on(['app.record.detail.show', 'app.record.edit.show'], (event) => {
    const record = event.record;
    const subAppId = '{生徒管理アプリID}';
    // 増殖バグ回避
    if (document.getElementById('student_list') !== null) {
      return event;
    }
    // To HTML escape
    const escapeHtml = (str) => {
      return str
        .replace(/&/g, '&')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#39;');
    };
    // スペースを取得
    const subtableSpace = kintone.app.record.getSpaceElement('student_list');
    // Rest API
    const params = {
      app: subAppId,
      query: `class_code in ("${record.class_code.value}") order by student_no asc limit 500`,
      fields: ['$id', 'student_code', 'student_name']
    };
    kintone.api(kintone.api.url('/k/v1/records', true), 'GET', params).then((resp) => {
      // success:生徒一覧を表示する
      const tableRecords = resp.records;
      let studentTable = `<table class="kintoneplugin-table">
      <thead>
        <tr>
          <th class="kintoneplugin-table-th" style="width: 250px;">
            <span class="title">コード</span>
          </th>
          <th class="kintoneplugin-table-th" style="width: 250px;">
            <span class="title">氏名</span>
          </th>
        </tr>
      </thead>
      <tbody>`;
      for (let i = 0; i < tableRecords.length; i++) {
        studentTable += `<tr>
          <td>
            <div class="kintoneplugin-table-td-control">
              <a href="/k/${subAppId}/show#record=${escapeHtml(tableRecords[i].$id.value)}" target="_blank">
                ${escapeHtml(tableRecords[i].student_code.value)}
              </a>
            </div>
          </td>
          <td>
            <div class="kintoneplugin-table-td-control">
              ${escapeHtml(tableRecords[i].student_name.value)}
            </div>
          </td>
        </tr>`;
      }
      studentTable += `</tbody></table>`;
      subtableSpace.innerHTML = studentTable;
    }, (error) => {
      // error:エラーの場合はメッセージを表示する
      let errmsg = 'レコード取得時にエラーが発生しました。';
      // レスポンスにエラーメッセージが含まれる場合はメッセージを表示する
      if (typeof error.message !== 'undefined') {
        errmsg += '\n' + error.message;
      }
      subtableSpace.appendChild(document.createTextNode(errmsg));
    });
    return event;
  });
})();

解説

レコード詳細表示およびレコード編集イベントのブロックに生徒一覧テーブル生成処理を記述します。

 9
10
  kintone.events.on(['app.record.detail.show', 'app.record.edit.show'], (event) => {
  });

REST APIを使って、生徒管理アプリからクラス登録した生徒の情報を取得します。
なお、queryのパラメーターには、クラスコードが一致するレコードのみを取得するように指定します。
取得するフィールドは、レコード番号、「生徒コード」、「生徒氏名」となります。

27
28
29
30
31
    // Rest API
    const params = {
      'app': subAppId,
      'query': `class_code in ("${record.class_code.value}") order by student_no asc limit 500`,
      'fields': ['$id', 'student_code', 'student_name']

レコードの取得が成功した場合に生徒一覧のテーブルを生成します。

33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
    kintone.api(kintone.api.url('/k/v1/records', true), 'GET', params).then((resp) => {
      // success:生徒一覧を表示する
      const tableRecords = resp.records;
      let studentTable = `<table class="kintoneplugin-table">
      .
      .
      .
      <tbody>`;
      for (let i = 0; i < tableRecords.length; i++) {
        studentTable += `<tr>
        .
        .
        .        
        </tr>`;
      }
      studentTable += `</tbody></table>`;
      subtableSpace.innerHTML = studentTable;

生徒コードをクリックすると該当の生徒レコードを表示するようにリンクを設定します。

52
53
54
              <a href="/k/${subAppId}/show#record=${escapeHtml(tableRecords[i].$id.value)}" target="_blank">
                ${escapeHtml(tableRecords[i].student_code.value)}
              </a>

取得した生徒レコードから、「氏名」を表示します。

なお、クロスサイトスクリプティングの対策として、特殊文字をエスケープ処理しています。

動作確認

「生徒管理アプリ」、「クラス管理アプリ」それぞれにいくつかデータを入力します。
生徒管理アプリから、いくつかクラスをルックアップで登録し、「クラスアプリ管理」上に生徒一覧のテーブルが表示されていることを確認します。

まとめ

こちらのQ&Aの回答 にあるように詳細アプリを作成して、標準機能で対応も可能です。
しかし、JavaScript APIでカスタマイズすれば、わざわざ詳細データ設定用のアプリを作成しなくて済みます。
コード自体も意外とシンプルですので、ぜひ試してみてください。

information

このTipsは、2022年7月版kintoneで動作を確認しています。