iT邦幫忙

0

表單關聯和資料表關聯,差在哪裡?

  • 分享至 

  • xImage
  •  

表單關聯和資料表關聯,差在哪裡?

四分鐘影片導讀:從使用者的一句話開始,挑了業務員,部門跟著來

polhem-form-relations

影片:https://youtu.be/xY_ZlEAAcoE

影片約 4 分鐘,英文旁白,YouTube 上可以切換英文或繁體中文字幕,以 Polhem 1.12.1 錄製。

前兩篇導讀的是整套流程:從 Excel 匯入定義,到桌面、網頁、手機上執行。這次只談一件事:表單之間的關聯怎麼設計。示範沿用上一支 Blazor Server 影片的 Northwind 專案,在訂單上加入員工與部門。


1️⃣ 使用者的一句話

問使用者一張訂單需要什麼,常聽到的是這樣一句話:

挑業務員,他的部門就跟著出來。他之後調部門,這張單仍歸原部門。

這句話就是整個需求。前半句是操作:挑一個人,相關的資料自己帶出來。後半句是資料的歸屬:訂單記下的是開單當時的部門,不會跟著員工異動。


2️⃣ 傳統做法:把這句話翻譯成資料表

在一般的系統裡,開發者要先把這句話翻成資料表和外鍵:orders.employee_id 指向 employees,employees.dept_id 再指向 departments。接著要寫三樣東西:

  • 查詢:清單要顯示員工和部門名稱,得寫 JOIN
  • 挑選視窗:讓使用者挑員工
  • 挑選後的處理:選了員工之後,把代號、名稱和部門抄進訂單的程式

後半句「仍歸原部門」也要另外想清楚:部門要存在訂單上,不能每次都從員工 JOIN 過去,否則員工一調部門,舊單的部門也跟著變。

這些工作每個用到員工的畫面都要再做一次。需求是一句話,實作分散在 SQL、畫面和程式碼裡,事後要從程式碼看出原本的需求並不容易。


3️⃣ Polhem 的做法:表單參照表單

Polhem 的關聯定義在表單(FormSchema,描述一張表單有哪些欄位的定義檔)上,內容是「這張表單參照那張表單」,不是「這張表的外鍵指向那張表的主鍵」。欄位上寫的是:挑了這筆,要帶回哪些欄位。這和使用者的說法是同一件事。

影片先打開 DefineEditor(Polhem 的定義檔編輯器)看既有的兩張表單:

  • Employee 的部門欄位參照 Department 表單,帶回部門的代號、名稱和主管
  • Department 的主管欄位又參照回 Employee

每張表單只描述自己直接參照的下一層。要從某張表單讀到更深一層的欄位時,框架會沿著這條鏈自己串出多層 JOIN,宣告的人只需要想一層。


4️⃣ 在訂單上寫下那句話

接著在 DefineEditor 打開 Order 表單,新增一個員工欄位,把它指向 Employee 表單。關聯對話框已經替員工的代號和名稱打好勾,這兩個是一般的顯示欄位。

前半句的「部門跟著出來」,是再勾選員工的部門欄位,把它們寫進訂單自己的部門欄位,並標成 LookupOnly(只在挑選時套用):挑選時填入,之後跟著訂單一起保存。

部門欄位本身設成唯讀,因為它一律由員工帶入,不讓使用者另外挑。它仍然參照 Department 表單,訂單才能顯示部門名稱,也能用部門查詢。

存檔後,Order 的定義大致是這樣:

<FormField FieldName="employee_rowid" Caption="Employee" DbType="Guid"
           RelationProgId="Employee" ValueChangeCallback="true">
  <RelationFieldMappings>
    <FieldMapping SourceField="sys_id"        DestinationField="ref_employee_id" />
    <FieldMapping SourceField="sys_name"      DestinationField="ref_employee_name" />
    <FieldMapping SourceField="dept_rowid"    DestinationField="dept_rowid"    LookupOnly="true" />
    <FieldMapping SourceField="ref_dept_id"   DestinationField="ref_dept_id"   LookupOnly="true" />
    <FieldMapping SourceField="ref_dept_name" DestinationField="ref_dept_name" LookupOnly="true" />
  </RelationFieldMappings>
</FormField>
<FormField FieldName="dept_rowid" Caption="Department" DbType="Guid"
           RelationProgId="Department" ReadOnly="true">
  <RelationFieldMappings>
    <FieldMapping SourceField="sys_id"   DestinationField="ref_dept_id" />
    <FieldMapping SourceField="sys_name" DestinationField="ref_dept_name" />
  </RelationFieldMappings>
</FormField>

讀起來就是使用者那句話:訂單屬於這位員工,也屬於他的部門。幾個值得多看一眼的地方:

  • RelationProgId="Employee" 指的是 Employee 表單,不是 employees 資料表。
  • 標了 LookupOnly 的三筆只在挑選時把值寫進訂單,不參與查詢時的 JOIN。訂單的部門由自己的 dept_rowid 透過下面那段關聯讀回來,所以讀到的是訂單上存的部門,不是員工目前的部門。後半句「仍歸原部門」就是靠這個區分做到的。
  • ValueChangeCallback 是對話框自動勾上的。挑選視窗只回傳員工的代號和名稱,部門不在其中,所以挑選後要請伺服器讀出那位員工的完整資料,把部門填回來。

5️⃣ Sync、Upgrade:不寫 SQL,也不寫程式

定義改好之後,在 DefineEditor 執行 Sync(同步資料表定義與版面),新欄位就加進資料表定義和表單版面;再按 Upgrade Database,訂單資料表的欄位就升級好了。整個過程沒有寫 SQL,也沒有寫程式。

打開訂單的版面,用網頁預覽看一下:表單上已經有員工,旁邊是唯讀的部門。


6️⃣ 在 Blazor Server 網站上試一次

最後到 Blazor Server 網站實際操作:

  1. 新增訂單,表單多了員工和唯讀的部門。
  2. 挑員工 Nancy Davolio,部門 Sales East 同時帶進來。
  3. 存檔,清單上這張訂單的員工是 Nancy,部門是 Sales East。
  4. 到員工表單,把 Nancy 調到 Sales West。
  5. 打開剛才那張訂單,部門仍是 Sales East,也就是開單當時她所在的部門。
  6. 再新增一張訂單、挑 Nancy,這次帶入的是 Sales West。

挑選員工後部門跟著帶入

使用者那句話的兩半,在這裡都看得到結果。


7️⃣ 適用範圍

這種做法涵蓋的是表單的新增、查詢、修改這類日常操作。報表、統計、批次作業需要任意形狀的 SQL,還是在業務物件裡自己寫。關聯也有限制:JOIN 一律是單一欄位的等值比對(外鍵對主鍵),對象是實體資料表,不支援子查詢或複合鍵。這些在文件的「適用與不適用場景」「限制與設計取捨」兩節有列出來。


✅ 結語

用資料表關聯,開發者要把使用者的話翻成 JOIN、挑選視窗和抄欄位的程式;用表單關聯,使用者的話直接寫成定義。好處是需求和實作對得起來:事後打開 Order 的定義,就能讀出「挑員工、帶回部門、部門跟著訂單保存」這件事,每個用到它的畫面行為都一樣。

這是我在 Polhem 裡選擇的做法,適合表單多、欄位和關聯常調整的企業系統。如果你的系統以報表或複雜查詢為主,或已經習慣用 ORM 管理資料模型,不一定需要換成這種方式,可以依專案情況判斷。

想自己試的話,範本用 dotnet new install Polhem.Templates 安裝,再用 dotnet new polhem-blazor-server 建立專案,文件與範例在 GitHub。

延伸閱讀


📘 HackMD 原文筆記:
👉 https://hackmd.io/@jeff377/polhem-form-relations

📢 歡迎轉載,請註明出處
📬 歡迎追蹤我的技術筆記與實戰經驗分享
Facebook | HackMD | GitHub | NuGet


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言