mcpField
N
nocobase-dsl-reconciler

nocobase-dsl-reconciler

github.com/nocobase開発55242 回インストール8 週間前に更新
リポジトリ

nocobase-dsl-reconciler について

nocobase-dsl-reconciler は、コミット可能な YAML DSL で NocoBase アプリを構築し、CLI でデプロイするオプトイン型スキルです。スターターからの段階的な構築やデプロイ前の検証を案内し、DSL の変更とライブ UI の編集を区別します。

主な機能

  1. 01スターターテンプレートからの構築
  2. 02YAML によるコレクション、ルート、ページ、ポップアップの定義
  3. 03CLI による NocoBase への変更反映
  4. 04グループやページを指定した段階的なデプロイ
  5. 05デプロイ前の DSL 仕様検証

活用シーン

  1. 01コミット可能な YAML による CRUD モジュールの構築
  2. 02コレクションやページの段階的な追加
  3. 03YAML 作成前の複数コレクション構成の設計
  4. 04指定ページへの DSL 変更のデプロイ

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/nocobase/skills --skill nocobase-dsl-reconciler

インストール後、Claude Code で/nocobase-dsl-reconcilerを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

exports/crm-opportunities/
structure.yaml
exports/crm-opportunities/js/
opportunities_filterForm_0_pipeline_stats_filter_block.js
exports/crm-v2/
enhance.yaml
exports/crm-v2/charts/
analytics_chart_1.json
analytics_chart_2.json
analytics_chart_3.json
analytics_chart_4.json
analytics_chart_5.json
exports/crm-v2/js/
analytics_filterForm_0_js_0.js
analytics_jsBlock_6.js
analytics_jsBlock_7.js
analytics_jsBlock_8.js
analytics_jsBlock_9.js
customers_filterForm_1_customer_stats_filter_block.js
customers_popup_name_tab0_contacts_contact_row_column_customer_360.js
customers_popup_name_tab0_details_0_customer_statistics_block.js
customers_popup_name_tab0_details_0_item_profile_customer_profile_header.js
customers_popup_name_tab0_details_1_ai_smart_profile_block.js
customers_popup_name_tab0_opportunities_opportunity_row_column_customer_360.js
customers_table_0_col_health_score.js
customers_table_0_col_last_activity.js
leads_filterForm_1_leads_stats_filter_block.js
leads_popup_name_tab0_details_0_lead_badges_row_component.js
leads_popup_name_tab0_details_0_lead_key_stats_block.js
leads_popup_name_tab0_details_5_lead_stage_flow_block.js
leads_popup_name_tab0_lead_insights_lead_ai_insights_block.js
leads_popup_name_tab0_lead_owner_lead_profile_header_block.js
leads_table_0_col_ai_score.js
leads_table_0_col_contact.js
leads_table_0_col_created.js

詳細

NocoBase アプリケーションビルダー(DSL パス)

このスキルを使用する前に

これはオプトインの DSL パスです。デフォルトは nocobase-ui-builder です。 ユーザーが、コミット可能な YAML ファイルと cli push を明示的に希望している場合に限り、このスキルを使用してください。ユーザーが DSL/YAML/git を指定せずに、一般的な「NocoBase アプリを作って」という依頼からここにたどり着いた場合は、デフォルトのエントリーポイントである nocobase-ui-builder に切り替えてください。

最も重要なルール

templates/crm/ は読み取り専用の参照ライブラリです。特定のフィールド、ブロック、ポップアップの形が分からない場合は、最も近い CRM の例を開いて読み、自分のワークスペースで調整したものを作成してください。シナリオごとの参照先は、以下のワークフローのセクションにあります。

CRM のファイルを丸ごとワークスペースにコピーしてはいけません。作業を始めるために cp -r templates/crm/... を実行したり、collections/nb_crm_*.yaml を複製したり、CRM の routes.yaml をベースにしたりしないでください。大量にコピーすると、関係のないリード/商談/注文の状態やワークフローまでプロジェクトに持ち込まれ、モジュールを作る代わりに、何百もの無関係なバリデーターエラーへの対処にセッションを費やすことになります。

デプロイ前の仕様バリデーターは、構造上のミスの大半を明確なエラーメッセージで検出します。バリデーターを信頼してください。エラーが出たら、推測せずに指摘された内容を修正してください。src/deploy/*.ts を grep しないでください。

このスキルを使用しない場合

ユーザーの依頼がこのスキルの対象外である場合は、該当するスキルに引き継いでください。

ユーザーの依頼…スキル
すでに稼働しているページの UI を一度だけ変更(単一のブロック、フィールド、アクションの移動/並べ替え/再設定)— DSL のコミットは不要nocobase-ui-builder
ACL/ロール権限/ルート権限nocobase-acl-manage
ワークフローの作成/更新/改訂/実行nocobase-workflow-manage(このスキルではトリガーボタンのみを配線します。グラフの作成はそちらで行います)
DSL プロジェクト外でのコレクション/フィールド/リレーションの作成nocobase-data-modeling
プラグイン開発(.tsx コンポーネント、サーバーコード)nocobase-plugin-development
プラグインのインストール/有効化nocobase-plugin-manage
環境のセットアップ/アプリのインストール/アップグレードnocobase-env-manage

workspaces/<project>/ 配下のコミット対象 YAML ファイルとして保存する変更は、すべてこのスキルで扱います。

環境

cd <skill-dir>/src
export [email protected] NB_PASSWORD=admin123 NB_URL=http://localhost:14000

クイックスタート — 新規構築

「NocoBase アプリを作って」という依頼のデフォルトパス:スターターをコピーして変更します。スケルトンを一から手書きしたり、最初に CRM を調べたりしないでください。

cd <skill-dir>/src
cp -r ../templates/starter ../workspaces/<name>

# First push: --copy bypasses validation rules that only matter once
# popups exist (m2o popup binding, clickToOpen file presence). The
# starter ships with its own popups so the first push is actually fully
# valid — --copy is for your *extensions* before popups are wired.
npx tsx cli/cli.ts push <name> --force

スターターは、最小限の CRUD が一通りそろった構成です — 1 つのコレクション(Projects)、1 つのダッシュボードページ(4 つの KPI タイル + 2 つのチャート)、filterForm/table/addNew ポップアップ/detail ポップアップ/2 つの updateRecord recordActions を備えた 1 つの一覧ページです。そのままプッシュ → NB 上で表示を確認 → その後に編集します。

スターターのカスタマイズ(アジャイルな反復)

一度に 1 つの項目だけを変更し、その都度プッシュします。

  1. 識別子の名前を変更して、ユーザーのドメインに合わせます。
    • collections/nb_starter_projects.yaml → 使用するコレクション名 (nb_<module>_<entity> の規則に合わせます)
    • routes.yaml:「Starter」/「Projects」のタイトルを変更します
    • 必要であれば pages/starter/ ディレクトリ名を変更します(routes.yaml のキーに合わせます)
    • すべてのファイルで nb_starter_projects を検索して置換します
  2. コレクション YAML のフィールドリストを、ユーザーのエンティティに合わせて調整します。pages/<...>/layout.yaml の fields: と、ポップアップテンプレートの field_layout: もそれに応じて更新します。
  3. 1 つ目のエンティティが最初から最後まで動作してから、2 つ目のエンティティを追加します。新しい collections/*.yaml と pages/<module>/<entity>/ ディレクトリを作成し、pages/starter/projects/layout.yaml を出発点としてコピーします。
  4. 段階的に拡張します:タブ、チャート、ワークフロートリガーを追加します。変更するたびにプッシュしてください。どのパターンにどの CRM ファイルが対応するかは、以下の「高度なパターン」を参照してください。

モジュール全体を一度に作成しないでください。 ユーザー向けの構築では、まずスケルトンを作成してユーザーに見せ、フィードバックを集めて反復します。スターターのプッシュは数分で済みますが、手作業で作るモジュールには何時間もかかります。

短時間で進める:--copy が役立つ場合

ワークスペースにまだポップアップファイルがない場合は、--copy を指定します(初期段階では、バリデーターが「m2o field X has no popup binding」というエラーを出しますが、ユーザーは次のプッシュで修正する予定です)。この状態では、リコンシリエーターが仕様エラーを自動的に回避します。popups/*.yaml が1つでも存在するようになったら、--copy を外してバリデーションを実行します。

差分編集 — 既存のワークスペース

  • 追加:ブロック/フィールド/アクション/ポップアップを追加する場合は、DSLを記述してプッシュします。
  • 削除:DSLから削除してプッシュします。リコンシリエーターはNB側の対応するライブモデルを削除し、state.yaml をクリーンアップします。NB-UIで手動作成された要素(state.yaml で追跡されていないもの)はそのまま残ります。
  • 名前変更:自動ではサポートされていません。削除してから追加してください。

対象を絞ったプッシュ:

  • --group <key> で1つのメニューサブツリーを対象にします
  • --page <key> で1つのページを対象にします
  • --incremental で、DSLが前回のプッシュから変更されていないページをスキップします

DSLのコミットを伴わない、ライブUIだけの調整では、代わりに nocobase-ui-builder に引き継いでください(上記のルーティング表を参照)。

高度なワークフロー — スターターでは不十分な場合

アジャイルなスターターループから先に進む条件:

  • 相互リレーション(m2m、ツリー構造)を持つコレクションが約3つ以上ある
  • ユーザーが事前に設計したい専用のワークフロー/承認/権限ロジックがある
  • 複数タブのページ、サブテーブル、またはモジュール間ナビゲーションがある
  • ユーザーのドメイン用語に対応付けた、独自のKPIを含むダッシュボードがある

進行手順:ラウンド0の設計 → ラウンド1のスキャフォールド → ラウンド2の入力 → ラウンド2'のシード → ラウンド3のJS。それぞれのラウンドはデプロイ可能な状態です。

ラウンド0:システムアーキテクチャ — ユーザーに確認する

DESIGN.md(YAMLではなくMarkdown)を作成し、次の内容を記載します。

  1. コレクション — すべてのテーブルと、そのフィールドおよびリレーション。 フィールドインターフェイスの参考情報は nocobase-data-modeling スキルを参照してください。
  2. ページ一覧 — すべてのページと、それぞれの目的を1行で記載し、メニューごとにグループ化します。
  3. ナビゲーションの配線 — どのm2oフィールドがどのポップアップテンプレートを開くか、またどのページ同士がリンクするかを記載します。

YAMLを記述する前に、ユーザーの確認を待ちます。設計を一度行うだけで、再設計を3回行う手間を省けます。

ユーザーの依頼がスターターの構成に収まる場合(単一エンティティ、基本的なCRUD)は、ラウンド0を省略します。

例:

## Collections
- nb_lib_books (title, author, isbn, category, status, loans: o2m → nb_lib_loans)
- nb_lib_members (name, email, phone, join_date, loans: o2m → nb_lib_loans)
- nb_lib_loans (loan_no, book: m2o, member: m2o, borrowed_at, due_date, returned_at, status)

## Pages (under menu "Library")
- Books list — browse + search books, add new
- Members list — browse members, their loan history
- Loans list — active/overdue loans, return action
- Dashboard — KPIs + charts

## Navigation
- books.table.title → books detail popup (shared template)
- loans.table.book → books detail popup (shared via defaults.yaml)
- loans.table.member → members detail popup (shared via defaults.yaml)
ラウンド0.5:サブエージェントのCWD(起動する場合のみ)

サブエージェント(kimi TUI、Claude Codeのサブプロセス)を起動すると、そのCWDがデフォルトの書き込み先になります。起動前に設定してください:

mkdir -p <user-workdir>
cd <user-workdir>
kimi --yolo       # or claude, codex

cd を省略すると、エージェントは親プロジェクトのルートに書き込みます。

ラウンド1:スキャフォールド — まずはスターターから始める

高度な手順でも、routes.yaml と collections/*.yaml を最初から手書きしないでください。スターターをコピーしてから拡張します。

手順作業内容
ベースcp -r templates/starter workspaces/<name> — 1回プッシュします
コレクションを追加collections/<next_coll>.yaml を記述します(形式はスターターに合わせます)。nb_<module>_<entity> の規則に合わせます。
ページを追加mkdir pages/<module>/<page>/ + layout.yaml。スターターのprojectsレイアウトを参考にします。
ルートを更新routes.yaml の既存グループにエントリーを追加します。
デプロイコレクション/ページを追加するたびに cli push <name> --force を実行します

CRMの参考資料(構造に迷った場合のみ参照):

  • templates/crm/collections/nb_crm_leads.yaml — コレクション形式
  • templates/crm/routes.yaml — 複数グループのルート(構成のみ)
ラウンド 2:コンテンツを埋める — ブロック、ポップアップ、テンプレート

スターターの基本を超える各ページについて:

作成するものCRM の参照先
メインリストのテーブル+フィルターtemplates/crm/pages/main/leads/layout.yaml
複数タブのページtemplates/crm/pages/main/customers/(page.yaml + tab_*/layout.yaml)
o2m の子要素用インラインサブテーブルを含む作成フォームtemplates/crm/templates/block/form_add_new_opportunities_quotations_quotations.yaml — items は fields: に記載された o2m フィールドで、編集可能なインラインサブテーブルとして表示されます。マスター/子要素の UX については templates/crm/pages/main/products/ も参照してください。
詳細ポップアップテンプレートtemplates/crm/templates/popup/activity_view.yaml
m2o の自動ポップアップバインディングtemplates/crm/defaults.yaml
親の詳細+子リストのポップアップtemplates/crm/pages/main/customers/tab_customers/popups/
addNew +フィールドクリックポップアップのパターンtemplates/crm/pages/main/leads/popups/

CRM からコピーする際のルール:

  • 10~30 行をコピーし、名前を調整してください。ファイル全体はコピーしないでください。
  • uid:/targetUid:/route_id: はコピーしないでください。デプロイヤーが新しい値を割り当てます。
  • テーブルに表示するすべての m2o フィールドについて、clickToOpen: templates/popup/popup_detail_<target>.yaml を設定するか、defaults.yaml に popups.<target>: ... を追加してください。そうしないと、バリデーターでエラーが発生します。
行ごとのアクション(recordActions)

デフォルトでは、テーブルの行アクション列は空です。recordActions: に列挙しない限り、NB はアクションボタンを描画しません。「編集と削除だけ」はよくあるものの不十分なデフォルト設定です。ほとんどのリストテーブルでは、ユーザーは実際には、編集の隣にワンクリックで状態を変更する操作(完了にする、承認、アーカイブ)を求めています。

判断の順序:

  1. ブール値または列挙型のステータスフィールドはありますか? → linkageRules を指定した updateRecord を追加し、レコードが適切な状態のときにだけ対応するボタンを表示します。状態遷移ごとにボタンを 1 つ用意します(すでに完了している場合は「完了にする」を非表示、未完了の場合は「再開」を非表示)。
  2. デフォルトの編集ポップアップとは異なる、2 つ目の詳細/フォームビューが必要ですか? → templateRef を指定した popup を使用します。
  3. ツリー/階層構造のコレクションですか? → addChild を追加します。
  4. このレコードの ID/フィルターを使って別の場所へ移動する必要がありますか? → url: /admin/...?filter={{ctx.record.id}} を指定した link を使用します。
  5. 複雑なレコードをユーザーが複製できるようにしますか? → duplicate を使用します。
  6. ワークフローを手動で開始する必要がありますか? → workflowTrigger を使用します。

状態変更には、カスタム JS ボタンより updateRecord + linkageRules を優先してください。linkageRules を使えば、JS に触れることなく、行レベルの UX の 80%(条件付きの表示/非表示、フィールドに基づく制御、ctx.user を使ったロールチェック)に対応できます。JS アクションが必要なのは、複数ステップのロジック(クエリしてから更新し、その後に移動)を行う場合のみです。また、現在このリコンサイラーではデプロイできません。

行ごとのアクションの一覧(recordActions: で宣言):

DSL の type目的CRM の参照先
edit編集ポップアップ(デフォルトの形式)多数
view読み取り専用の詳細ポップアップtemplates/crm/templates/popup/opportunity_view.yaml
delete確認付きの単一行削除多数
updateRecordフィールドを割り当てる(linkageRules は任意)— 状態変更の主役templates/crm/pages/main/overview/layout.yaml(完了/未完了のペア)
popuptemplateRef を使ってカスタムポップアップを開く(編集とは異なるフォーム/詳細)templates/crm/pages/main/leads/popups/table.name.yaml
linkレコードのコンテキストを URL に含めて、別の管理ページへ移動templates/crm/pages/lookup/layout.yaml
addChildツリーコレクション:この行の下に子ノードを追加templates/crm/pages/main/products/tab_categories/layout.yaml
duplicateレコードを新しいフォームに複製—
workflowTriggerこのレコードのワークフローを手動でトリガー(templates/crm/pages/main/customers/tab_customers/layout.yaml のツールバー — 同じ形式を行ごとにも使用できます)
historyExpand / historyCollapseレコード履歴プラグインのインライン展開—
aiAI 従業員ボタン(tasks_file + employee)templates/crm/pages/main/leads/layout.yaml(ツールバー — 同じ形式を行ごとにも使用できます)

ツールバーアクション(ブロックレベルの actions: で宣言)は、同じタイプ名に加えて filter、refresh、addNew、bulkDelete、export、import を使用します。すべてのタイプがどちらのコンテキストでも使えるわけではありません。ツールバーアクションとしての updateRecord は特定の行に適用されないため、recordActions に指定してください。

Round 2 の目標:すべてのページで CRUD を利用できるようにします。追加/編集/表示ポップアップを正しく接続し、行アクション列に実際のレコードごとの操作を反映します(編集と削除だけにしない)。バリデーターのエラーをなくし、NB UI に「コレクションが削除された可能性があります」というバナーが表示されないようにします。

Round 2':テストデータ(Round 2 と並行)

ページの作成と並行して実行できます。API 経由でデータを挿入します:

TOKEN=$(curl -sS -X POST $NB_URL/api/auth:signIn \
  -H 'Content-Type: application/json' -H 'X-Authenticator: basic' \
  -d '{"account":"'$NB_USER'","password":"'$NB_PASSWORD'"}' \
  | python3 -c 'import json,sys;print(json.load(sys.stdin)["data"]["token"])')

# Always GET existing record IDs first — they're snowflake integers
# (e.g. 359571523764224), NEVER 1/2/3.
curl -sS -X POST $NB_URL/api/<collection>:create -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' -d '{...fields..., "owner":{"id": <real-user-id>}}'

親テーブルから先に挿入し、子テーブルではすべての FK を設定します(null のままにすると、その行は孤立します)。その後:

npx tsx cli/cli.ts verify-data <name>     # FK & completeness check

並行して行う理由:Round 3 の JS(チャート、KPI)で何かを表示するにはデータが必要です。コレクションができたら(Round 1 の終わり)、シード処理を開始してください。Round 2 でページの作成が終わる頃には、テスト用のレコードが揃っています。

ラウンド 3:JS — CRM で使われている場所では、あなたのプロジェクトでも必要になる可能性があります

CRUD とデータ処理が動作したら、JavaScript が役立つ場所を確認します。CRM テンプレートを見て、プロジェクト内の各箇所について「CRM ではここに JS があるか?」と確認してください。 JS がよく使われる箇所は次の 3 つです。

箇所CRM に JS はあるか?次に当てはまる場合、プロジェクトでも JS が必要になる可能性があります
フィールドレンダラー/列(例:色分けされたステータスタグ、期限までの日数を示すバッジ)✅ ほとんどのリストテーブルにあります派生値(日付計算、ステータスに応じた色分け、複数フィールドの合成)によって表示が変わるフィールドがある
ブロック(KPI カード、フォーム内のカスタムウィジェット)✅ 概要、分析、フォームごとのヒントにあります複数のコレクションから読み込むサマリーウィジェット、インラインチャート、「ヘルパーパネル」が必要
ダッシュボードページ(チャートと KPI を並べたページ全体)✅ 分析ページにありますモジュールにユーザーが重視する測定可能な指標が 3 つ以上ある。バリデーターでは、「Dashboard」/「Analytics」というタイトルのページに 5 つ以上のチャートが必須です

まず、該当する CRM ページとその js/ および charts/ フォルダーを grep して、CRM が JS を追加している場所を確認します。次に、下の単一ファイルの表を参考にして、プロジェクト用の JS ファイルを作成します。

ダッシュボードを独自に設計すると見栄えが悪くなりがちです。CRM の構成を参考にしてください。 まず参照レイアウトを開き、ブロック数、順序、グリッド幅を自分の layout.yaml にコピーしてから、各リーフファイルにコンテンツを追加します。

参照レイアウト参考にする内容
templates/crm/pages/main/overview/layout.yaml概要:1 つの jsBlock ヒーロー行、その下に小さなテーブルが 2 つあります。少数の KPI を表示するランディングページに使います。
templates/crm/pages/main/analytics/layout.yamlフルダッシュボード:filterForm の行 → 1 行に 4 つの KPI jsBlock → 16/8 ∣ full ∣ 14/10 グリッドに 5 つのチャートがあります。5 つ以上のチャートを表示する場合に使います(ページタイトルに dashboard または analytics が含まれる場合、バリデーターで必須です)。

手順:

  1. 参照用の layout.yaml を開きます。ブロックのキー/タイプ/幅を確認します。
  2. 同じ構成でプロジェクト用の layout.yaml を作成します。ブロック数と layout: セクションのグリッド幅は同じにし、ブロックキーとコレクション名は独自のものにします。
  3. 各ブロックのリーフ JS/SQL ファイルは、下の表からコピーします。ファイルは個別にコピーしてください。フォルダーを cp -r でコピーしないでください。
コピーするリーフファイル参照元
KPI カード jsBlocktemplates/crm/pages/main/overview/js/overview_jsBlock.js
フィルター済みサマリー jsBlocktemplates/crm/pages/main/analytics/js/analytics_jsBlock.js
チャート SQL(グループ化した件数)templates/crm/pages/main/analytics/charts/analytics_chart_2.sql
チャートの描画(echarts の棒グラフ/円グラフ)templates/crm/pages/main/analytics/charts/analytics_chart_2_render.js
filterForm のフィルター統計ボタンtemplates/crm/pages/main/customers/tab_customers/js/customers_customers_filterForm_customer_stats_filter_block.js
ページ全体のカスタム UI(ウィザード/複数ステップ/カスタムフロー)templates/crm/pages/main/customers/tab_merge/js/customers_merge_jsBlock.js — ページ全体が 1 つの type: jsBlock で構成されており、React で約 580 行です

各リーフファイルをコピーした後:

  • コピー先で名前を変更し、SQL/コレクション/フィールド名を適切なものに置き換えます。
  • モジュールに i18n がない場合は、ns: 'nb_crm' の i18n ラッパーを削除します。
  • ページの filterForm で使うフィールドキーが異なる場合は、ctx.var_form1.* のフィルター変数参照を簡略化します。

SQL チャート:保存してから実行する 2 段階のパターンを使います — ctx.sql.save({uid, sql}) の後に ctx.sql.runById(uid) を呼び出します。

基本概念

2 つの識別子:key と title

key = lower_snake_ascii 形式の識別子です。pages/ 配下のディレクトリ名と state.yaml のエントリーを決定します。タイトルが ASCII 文字のみでない場合(中国語やスペースを含み、スラッグ化すると意味不明になる場合)は、必ず明示的に指定してください。

title = ユーザーに表示するテキストです。

- key: it_ops
  title: IT 运维
  type: group
  children:
    - key: tickets
      title: 工单
2 種類のポップアップモード:key: reference と単独の ref:

key: reference — ポップアップブロックはテンプレートへの参照です。 テンプレートを編集すると、それを参照しているすべてのポップアップが更新されます。共有の追加/編集フォームにはこちらを使用してください。

blocks:
  - ref: templates/block/form_add_new_tickets.yaml
    key: reference           # REQUIRED for shared refs

単独の ref:(key: reference なし)— テンプレートの内容はポップアップごとにインライン展開され、各コピーは独立しています。ページファイルから大きなブロックを切り出す場合にのみ使用してください。

デプロイ後、共有テンプレートの usageCount は 1 以上になるはずです。0 のままの場合は、key: reference を指定し忘れています。

自動作成されるカラム — 宣言しないでください

NocoBase は次のカラムを自動作成します。宣言すると、通知なしにフィルタリングされたり、型の競合が発生したりします。

  • システムカラム:id、createdAt、updatedAt、createdBy、updatedBy
  • m2o / o2m の外部キー(FK)カラム:owner: m2o → users を宣言すると owner_id が自動作成されます。2 つ目の owner_id: integer の行を追加しないでください
  • m2m の結合テーブル:through: nb_x_y は自動作成されます。そのため、このテーブル用のコレクション YAML は記述しないでください
テーブルとサブテーブル — 異なるものなので、混同しないでください
テーブルサブテーブル
概要親レコードの子レコード用の完全な CRUD ブロック(フィルター+一覧+追加/編集ポップアップ)親フォームの内部にある、子行をインライン編集するグリッド
DSLtype: table + resource_binding.sourceId + associationNamecreateForm/editForm の fields: 内にある { field: tasks, type: subTable, columns: [...] }
使用場所詳細ポップアップ、タブページ、独立した一覧ポップアップcreateForm/editForm の内部
使用する場面子レコードを個別に参照する場合(顧客詳細 → 注文一覧)子レコードを親と一緒に入力する場合(請求書+明細)

フォーム内の単独の - tasks は 3 つ目の選択肢である RecordSelect ピッカー (「既存のレコードを選択」)です。通常は意図したものではなく、バリデーターが警告を表示します。

標準的な CRM の例:

  • テーブル(独立した CRUD ブロック):templates/crm/pages/main/customers/tab_customers/popups/table.name.yaml
  • サブテーブル(インライン編集可能なグリッド):templates/crm/templates/block/form_add_new_opportunities_quotations_quotations.yaml(items)
foreignKey で意味が切り替わります

m2o では、foreignKey は現在のテーブルにある FK カラムの名前です (owner: m2o, foreignKey: owner_id → SELF 上の owner_id)。

o2m では、foreignKey は対象テーブルにある FK カラムの名前です (tasks: o2m → nb_pm_tasks, foreignKey: project_id → nb_pm_tasks 上の project_id)。

コマンドリファレンス

cd <skill-dir>/src
export NB_USER=... NB_PASSWORD=... NB_URL=...

npx tsx cli/cli.ts push <name> --force          # deploy DSL → NocoBase
npx tsx cli/cli.ts push <name> --group <key>    # only one subtree
npx tsx cli/cli.ts push <name> --incremental    # skip unchanged (git diff)

npx tsx cli/cli.ts pull <name>                  # NocoBase → DSL (full round-trip)
npx tsx cli/cli.ts diff <left> <right>          # compare two DSL trees
npx tsx cli/cli.ts duplicate-project <src> <dst> --key-suffix _v2

npx tsx cli/cli.ts verify-data <name>           # FK / completeness check

push と pull はどちらも一方向です。往復処理は push + pull + git diff です。

よくあるエラー

エラー対処方法
fields not in collectionフィールド名がコレクション YAML と一致していません
titleField is missingtitleField: <field> を設定するか、name/title フィールドを追加してください
一部のページしかデプロイされないkey が pages/<key>/ ディレクトリと一致していません
作成時に string violation が発生するYAML で createdAt/updatedAt が宣言されています — 削除してください
チャートの SQL が失敗する先にシードデータを投入してください。"createdAt" のようにフィールド名を引用符で囲んでください
UI で m2o リンクが 400 になる対象コレクション用の defaults.yaml の popups: バインディングがありません
Collection X not found in data source mainassociationName に短縮名が使われています — コレクション名全体(project.tasks ではなく nb_pm_projects.tasks)を使用してください。templates/crm/pages/main/customers/tab_customers/popups/ を参照してください
行ごとのカラムに編集と削除しか表示されないrecordActions がありません — 追加する内容については、以下の「行ごとのアクション」を参照してください。DSL からアクション/フィールド/カラムを削除すると、次回の push で NB 側からも削除されるようになりました。

上記の内容と実行時の挙動が異なる場合、マニュアルが古くなっています。不足している内容を記録し、ユーザーに伝えてください。

コメント