いつ使う: 1:1 で対応する2つのレコードが 互いを参照し合う 関係。
具体例:
- 会社 ↔ 代表者
- 注文 ↔ 領収書
- ユーザー ↔ 拡張プロフィール
純粋な DB 設計だけ見ると 1:1 は片方向 FK が定石ですが、CLB の LinkField / ModuleField が 単方向 FK 前提で動くため、両側から相手を参照したいケースでは双方向 FK にしておく方が CLB の機能を素直に活かせます。
mutual_main mutual_sub
├── id PK ←───────────────┤ ├── id PK
├── name ├── name
└── sub_id FK → mutual_sub.id └── main_id FK → mutual_main.id ←┐
│
(mutual_main.sub_id) ───────────────────────────────────────────┘
両側に相手への FK 列がある。
| モジュール | テーブル | 役割 | 主な参照 |
|---|---|---|---|
MutualMain |
mutual_main |
親 | SubSlot (ModuleField → MutualSub, DbColumn=sub_id) で子を内包 |
MutualSub |
mutual_sub |
子 | Main (LinkField → MutualMain, DbColumn=main_id) で親を逆参照。DataOnlyFields に入れて UI には出さない |
- 親に
ModuleField、子にLinkFieldを置いて両側に FK 列を持たせる - 親の
.mod.csのOnAfterInitializationで 新規時に子の親参照に自分のテンポラリ ID をセット:
void OnAfterInitialization()
{
if (IsNewData)
{
SubSlot.ChildModule.Main.Value = this.Id.Value;
}
}- 親の Submit を 1 回押すと、CLB が 片方を NULL Insert → 後追い UPDATE で実 ID を埋めることで双方向サイクルを自動解決。アプリ側コードに特別な配慮は不要
- 双方向の FK 列のどちらか (もしくは両方) が NULL 許容であること (片方を NULL Insert するため)。NOT NULL 制約が両方に付いてると CLB の解決ロジックでも DB エラーになる
サイドバー データ操作/双方向ID (1:1) → MutualMain + MutualSub
- 「親 (Main) の Submit ボタン」だけで両方保存される。子側に Submit ボタンを置く必要なし
- 子の
MainフィールドをDataOnlyFieldsに入れず UI に出してしまうと、ユーザーが手動で親を選ぶ必要があるように見えて混乱しやすい
- アプリ作成パターン一覧 ─ 全パターンのインデックス
- モジュール定義の全体構造
- Field リファレンス ─ LinkField / ListField / DetailListField / ModuleField 等の詳細
