kintone で条件付バリデーション

## kintone とは？

- kintone については[こちら](https://kintone.cybozu.co.jp/what_is_kintone/)に詳しく掲載されていますのでご確認下さい

## 条件付バリデーションとは何か？
- 何らかの条件が成り立つ場合のみ、値が妥当かどうか検証する仕組み
- ここで言う条件付バリデーションとは特定のフィールド内で特定の値を持つレコード間でのみ重複禁止を指す
  - 具体的には下記の従業員DBの雇用状態が **在職中**、**休職中** のレコード間ではニックネームの重複を禁止する
    - 例: id が **1** のレコードの場合
      - ニックネームを **せと**、**ぼんこつ** に変更することはできない
      - ニックネームを **あんず**、**ほんだ** に変更することはできる(**退職済**のため)

## 従業員DB
- ()内は[フィールドコード](https://jp.cybozu.help/k/ja/user/app_settings/form/autocalc/fieldcode.html)

|id|雇用状態<br>(emp_status)|   姓<br>(last_name)   |  名<br>(first_name)  | ニックネーム<br>(nickname)   |
|----|------|------|------|-------------|
|1|在職中|  武藤  |  遊司  |  ゆう  |
|2|在職中|  海原  |  瀬人  |  せと  |
|3|休職中| 城之内 |  和也  | ぼんこつ   |
|4|退職済|  藤崎  |  杏子  | あんず   |
|5|退職済|  本田  |  ヒロシ  | ほんだ   |

## なぜ条件付バリデーションを行うのか？
- 文字列(1行)のフォーム作成の際に kintone 標準ではユーザからの入力に対してかけられるバリデーションは **必須項目にする**、 **値の重複を禁止する** ぐらいしかない為、例えば **ひらがなのみ登録可**、**特定の条件下でのみ重複禁止** というようなバリデーションを行うことができない。<br>よって、このようなバリデーションを行いたい場合はカスタマイズしたり、プラグインを導入したりする必要がある。
    - バリデーションによっては [gusuku Customine](https://docs-customine.gusuku.io/ja/) で実現することもできるが今回は自身でカスタマイズを行う。


## 実際のコード
```js
(() => {
    'use strict';

    const EMPLOYED = '在職中';
    const ABSENT   = '休職中';
    const RETIRED  = '退職済';

    const EMP_INCUMBENT_STATUSES = [EMPLOYED, ABSENT];

    let recordPrev;

    kintone.events.on([
        'app.record.index.edit.show',
        'app.record.create.show',
        'mobile.app.record.create.show',
        'app.record.edit.show',
        'mobile.app.record.edit.show',
    ], (event) => {
        recordPrev = event.record;
    });

    kintone.events.on([
        'app.record.index.edit.submit',
        'app.record.create.submit',
        'mobile.app.record.create.submit',
        'app.record.edit.submit',
        'mobile.app.record.edit.submit',
    ], (event) => {
        let record = event.record;
        // 未入力の場合はチェックしない
        if (record.nickname.value === undefined) {
            return event;
        }
        // ニックネームに変更がない場合(ニックネーム以外の項目を変更した場合など)はチェックしない
        if (recordPrev.nickname.value === record.nickname.value) {
            return event;
        }
        // 退職済の場合は重複してもいいのでチェックしない
        if (record.emp_status.value === RETIRED) {
            return event;
        }
        let params = {
            'app': kintone.app.getId(),
            'query': 'nickname = "' + record.nickname.value + '" and emp_status in ("' + EMP_INCUMBENT_STATUSES.join('","') + '")',
            'fields': ['last_name', 'first_name']
        };
        return kintone.api(kintone.api.url('/k/v1/records', true), 'GET', params).then((response) => {
            if (response.records.length > 0) {
                record.nickname.error = "下記の方と重複しています。\n\n";
                record.nickname.error += response.records.map(r => r.last_name.value + ' ' + r.first_name.value).join("\n");
            }
            return event;
        }, (response) => {
            record.nickname.error = '取得できませんでした';
            console.log(response);
            return event;
        });
    });
})();
```
- エラーメッセージの見せ方は好みにあわせてカスタマイズして下さい。
- 不具合や同じことをするのであればこう書いたほうが良いというものがあればコメント欄にてお知らせ下さい。
  - 個人的には `recordPrev` (変更前の状態)が show 系イベント内ではなく、 submit 系イベント内で取得できたら良いのになと思ってます。😅


## 参考文献
- [kintone API で Promise を使ってみよう！](https://developer.cybozu.io/hc/ja/articles/204564604-kintone-API-%E3%81%A7-Promise-%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%81%BF%E3%82%88%E3%81%86-)