開発者向け機能:スクリプト:$p.siteId
## 概要
サイトID値を取得することができます。
```
$p.siteId(siteTitle)
```
### パラメータ
|パラメータ名|型|必須|概要|
|:--|:--|:--|:--|
|siteTitle|string||省略可能、サイトのタイトルで確認したい場合は文字列を指定|
### 戻り値
引数省略時は本メソッドを実行したサイト(フォルダ、テーブル、テーブルに登録されているレコードのエディタ、Wiki、ダッシュボード)のサイトIDを返却します。
引数設定時は本メソッドを実行したサイトのタイトルと一致する場合はそのサイトのサイトIDを返却します。一致しない場合はundefinedとなります。
### 使用例
【例】サイトID:100のサイトの場合
```javascript
// 引数省略時
$p.siteId();
// 戻り値
100
// 引数設定時:タイトル一致
$p.siteId(’WBS’);
// 戻り値
100
// 引数設定時:タイトル不一致一致
$p.siteId(’課題管理’);
// 戻り値
undefined
```
## サンプルコード
<details>
<summary>1.「タイトル+対象日+作成者」の組み合わせによる条件付き重複チェック</summary>
タイトルと対象日を記入後、サイト内に同じタイトル・対象日が属する月の既存レコードがないかを検索します。
自分自身が作成した組み合わせのみを重複とみなし、保存を中断します。他のユーザーが同じ組み合わせを使っていても対象外です。
### 判定基準
| 作成者が自分 | タイトル一致 | 対象日が同じ月内 | 判定 |
|:-:|:-:|:-:|:-:|
| × | - | - | 保存可 |
| ○ | × | - | 保存可 |
| ○ | ○ | × | 保存可 |
| ○ | ○ | ○ | エラー |
### 実務での使いどころ
経費申請や出張申請のように、同じ月に複数件の正当な申請があり得る業務で、操作ミスによる二重登録を防ぐのに使えます。
単純な「日付完全一致」ではなく「対象日が同じ月かどうか」で判定するため、「同じ内容(タイトル)の申請を、月内の別の日にもう一度出してしまう」という、日付ピンポイントの一致では拾えないミスも検出できます。
他の申請者が同じ月に同じタイトルの申請をすること自体は業務上問題にならないため対象外とし、あくまで「自分自身の操作ミス」だけを拾う仕様です。
本サンプルは、「対象日が属する月」という期間ベースの判定を組み込むことで、単純な文字列結合キーでは表現できない業務要件に対応しています。
※標準の[重複禁止](/ja/manual/table-management-no-duplication)機能との併用は避けてください(1項目単位・全レコード対象で常時ブロックされるため、本サンプルの「期間ベースの判定」「自分自身に限定」が反映されません)。
### 事前準備
1: テーブルにタイトル項目(標準のタイトル)と、日付項目(対象日、項目コード:DateA)を用意してください。
2: スクリプトを登録します。
2-1: 「テーブルの管理 > スクリプト」タブで「新規作成」を選択する。
2-2: タイトル: 任意の名前(例:タイトル・対象月の重複チェック)
2-3: スクリプト: 下記サンプルコードを貼り付け
2-4: 出力先: 「新規作成」と「編集」の両方にチェック
##### JavaScript
```javascript
// ===== 設定 =====
const SITE_ID = $p.siteId();
const TITLE_COLUMN = 'Title';
const DATE_COLUMN = 'DateA';
const OWNER_COLUMN = 'Owner';
const buildDuplicateMessage = (matchedId) => `ID:${matchedId}と同一の内容です。`;
// ===== 状態 =====
let hasDuplicate = false; // 現在の入力内容が重複しているかどうか
let duplicateMessage = ''; // 表示するエラーメッセージ
let eventsBound = false; // change イベントを登録済みかどうか(多重登録防止)
// 対象日の文字列("yyyy/MM/dd"等)から、その月の初日・末日を求める
function getMonthRange(dateStr) {
if (!dateStr) {
return null;
}
const [year, month] = dateStr.split('T')[0].split(/[/-]/).map(Number);
const firstDay = new Date(year, month - 1, 1);
const lastDay = new Date(year, month, 0);
return { firstDay, lastDay };
}
// Date型を"yyyy/MM/dd"形式の文字列に変換する(検索条件の指定に使用)
function formatDate(date) {
const parts = [date.getFullYear(), date.getMonth() + 1, date.getDate()];
return parts.map((n, i) => i === 0 ? n : String(n).padStart(2, '0')).join('/');
}
// 1件のレコードが、入力中の内容と重複しているかを判定する
// 条件:作成者が自分、タイトルが一致、対象日が同じ月内
function isSameRecord(record, title, monthRange, currentUserId) {
if (record[OWNER_COLUMN] !== currentUserId || record[TITLE_COLUMN] !== title) {
return false;
}
const recordDateStr = record.DateHash ? record.DateHash[DATE_COLUMN] : null;
if (!recordDateStr) {
return false;
}
const [ry, rm, rd] = recordDateStr.split('T')[0].split(/[/-]/).map(Number);
const recordDate = new Date(ry, rm - 1, rd);
return recordDate >= monthRange.firstDay && recordDate <= monthRange.lastDay;
}
// タイトル・対象日を元に、サイト内に重複するレコードがないか検索する
// 結果は hasDuplicate / duplicateMessage に保存しておき、保存時の判定に使う
function checkDuplicate(ownRecordId) {
const title = $p.getValue(TITLE_COLUMN);
const targetDateStr = $p.getValue(DATE_COLUMN);
const currentUserId = $p.userId();
hasDuplicate = false;
duplicateMessage = '';
$p.clearMessage();
const monthRange = getMonthRange(targetDateStr);
if (!title || !monthRange) {
return;
}
$p.apiGet({
id: SITE_ID,
data: {
View: {
ColumnFilterHash: {
Title: title,
[DATE_COLUMN]: `${formatDate(monthRange.firstDay)},${formatDate(monthRange.lastDay)}`
}
}
},
done: function (data) {
const allRecords = (data && data.Response && data.Response.Data) || [];
const exactMatches = allRecords.filter(function (record) {
if (ownRecordId !== null && record.ResultId === ownRecordId) {
return false;
}
return isSameRecord(record, title, monthRange, currentUserId);
});
if (exactMatches.length > 0) {
hasDuplicate = true;
duplicateMessage = buildDuplicateMessage(exactMatches[0].ResultId);
$p.setMessage('#Message', JSON.stringify({
Css: 'alert-error',
Text: duplicateMessage
}));
}
},
fail: function () {
console.log('重複チェックのための検索に失敗しました。');
}
});
}
// 画面表示時:現在の内容で一度チェックし、以後はタイトル・対象日の変更のたびに再チェックする
$p.events.on_editor_load = function () {
const action = $p.action();
if (action !== 'new' && action !== 'edit') {
return;
}
const ownRecordId = action === 'edit' ? $p.id() : null;
checkDuplicate(ownRecordId);
if (eventsBound) {
return;
}
eventsBound = true;
$p.on('change', TITLE_COLUMN, function () {
checkDuplicate(ownRecordId);
});
$p.on('change', DATE_COLUMN, function () {
checkDuplicate(ownRecordId);
});
};
// 保存直前:重複していれば保存を中断する
$p.events.before_validate_Create =
$p.events.before_validate_Update = function (args) {
if (hasDuplicate) {
$p.clearMessage();
$p.setMessage('#Message', JSON.stringify({
Css: 'alert-error',
Text: duplicateMessage
}));
return false;
}
return true;
};
```
##### 検証データ
````
ID:2001 タイトル: 会議費 対象日: 2026/07/15 作成者: ユーザーA
ID:2002 タイトル: 交通費 対象日: 2026/08/03 作成者: ユーザーA
ID:2003 タイトル: 出張費 対象日: 2026/08/05 作成者: ユーザーB
ID:2004 タイトル: 交際費 対象日: 2026/08/10 作成者: ユーザーA
````
##### 実行結果1(ユーザーAがタイトル「会議費」・対象日「2026/08/28」と入力して保存)
メッセージは表示されない。
(自分の登録ID:2001とタイトルは一致するが、ID:2001は7月のため対象月が異なる。保存可能)
##### 実行結果2(ユーザーAがタイトル「開発費」・対象日「2026/08/10」と入力して保存)
メッセージは表示されない。
(自分の登録ID:2004と対象日は近いが、タイトルが「交際費」で一致しないため対象外。保存可能)
##### 実行結果3(ユーザーAがタイトル「出張費」・対象日「2026/08/05」と入力して保存)
メッセージは表示されない。
(ID:2003はタイトル・月とも一致するが、作成者がユーザーBのため対象外。保存可能)
##### 実行結果4(ユーザーAがタイトル「交通費」・対象日「2026/08/20」と入力して保存)
````
ID:2002と同一の内容です。
````
(作成者・タイトルが一致し、対象日も8月内のため、エラーとなり保存が中断される)
</details>



