Ruby on Rails:善用enum及human_attribute_name方法印出易讀的下拉選單內容
本篇文章包含四個部分:ActiveRecord::Enum說明及使用、human_attribute_name說明及使用、製作易讀的下拉選單、創建model共用模組印出選單選項,以下為詳細內容。
ActiveRecord::Enum說明及使用
在開發各式各樣的網站時,常會遇到要實作下拉選單功能,如果下拉選單的選項不少,又需要存到資料庫表單中,為了方便,通常會設計各個選項用數字儲存,例如電商網站儲存訂單狀態,各種狀態可能就會用不同的整數存進訂單資料表的欄位,像下方這樣
直接在rails console任意撈取一筆訂單資料看看,查詢status,結果會像下面這樣
查詢status後,直接看到數字,根本無法直接得知各個數字代表的意思,到底3是代表付錢了還是作業中呢,實在無法從數字的結果得知,想要讓屬性更好辨識,這時候可以使用Rails的ActiveRecord內建的enum模組,根據ActiveRecord::Enum的官方文件,只要在model定義好特定欄位各種數字狀態所代表的意思,之後查詢資料時,就可以直接看到對應的意思,而不是數字了,例如在訂單Order這個model,先定義好狀態status的enum,像下面這樣
在rails 的console隨意撈取一筆資料
可以看到,已經不是出現3了!而是直接顯示在Order model 我們定義status的enum中3代表的意思 — shipping。
enum也可以讓我們直接檢查資料是不是特定狀態,例如這邊Order model隨機抓取一筆資料,使用not_paid?就可以確認一筆訂單是不是未付款狀態
其他定義在status enum的狀態像是paid, shipping..等等,也可以直接使用paid? shipping?這樣的方法來確認訂單狀態。
如果想直接查看status底下到底有多少狀態,enum也提供了方法讓我們去呼叫,規則就是model class去呼叫已經定義enum欄位複數名稱,像status的話就是statuses,所以這邊查看Order的status有多少狀態,就會寫成下面這樣
回傳的結果是一個hash,內容是我們定義在model中的enum的內容,就可以看到這個欄位有多少狀態了。
human_attribute_name說明及使用
雖然使用enum後,Order的status已經可以顯示好懂的內容,但是似乎還不是一個好看的格式,例如not_paid這樣包含下底線的顯示似乎不太好看,想要讓這樣的格式升級成為更專業的樣子,這時候就可以使用ActiveRecord內建的另一個方法 — human_attribute_name,根據官方文件,human_attribute_name這個方法可以將model的屬性轉化為更為人性化的格式,例如”first_class”就會轉換為“First Class”,直接來看一下使用human_attribute_name方法後Order model這邊status屬性為not_paid的顯示結果
not_paid直接被轉為Not paid了,human_attribute_name預設會將字串的連接符號換成空白,然後將第一個字改成大寫,如果不想使用human_attribute_name預設的轉換格式,則可以將對應的格式定義在locale檔案中,例如not_paid想轉換為Not Paid,就可以像下面這樣定義在locale檔案
從rails console查看使用human_attribute_name的結果
可以看到,not_paid就不是human_attribute_name預設的格式,變成在locale定義的格式 Not Paid了。
製作易讀的下拉選單
大致了解enum以及human_attribute_name如何使用,接著就可以利用他們來實作下拉選單的選項了,先單純使用enum提供的方法試試,把Order的status都印出來,作為下拉選單的項目,程式碼可能會像下面這樣
直接使用Order.statuses把所有的status內容都印出來,網頁上就會看到像這樣的下拉選單
看起來已經不錯了,但是not_paid看起來就是有點突兀,這個時候再搭配human_attribute_name讓下拉選單內容更美觀,可以先用human_attribute_name將選單內容組出,程式碼像下面這樣
從rails console直接執行這段程式,組出來的資料像下面這樣
human_attribute_name轉換後的結果跟對應的enum status都放進一個array了,把這個結果放到下拉選單的選項看看,程式碼像下面這樣,controller先把下拉選項組出,然後view直接讀取
網頁上看到的下拉選單就會像下面這樣
呈現出來的格式已經是比較正式的格式了,而不是原始碼的感覺。
創建model共用模組印出選單選項
基於現實的開發狀況,專案中應該不只一個model會需要實作下拉選單,假設Order model還有一個欄位shipment_mode也是要做下拉選單,還有Product model也有欄位category也可能有此需求,若兩個model都個去寫一段程式組出下拉選單內容,感覺好像是做重複的事情,寫重複的程式,不符合Ruby的風格 — Don’t repeat yourself,這時可以統一寫一個模組,內建組出enum及human_attribute_name轉換結果的方法, 之後model引用這個模組再呼叫同一個方法就可以組出各自需要的選單內容,得到像Order model的欄位status下拉選單選項的資料格式
[[“Not paid”, “not_paid”], [“Paid”, “paid”], [“Shipping”, “shipping”], [“Deliverd”, “deliverd”], [“Returned”, “returned”], [“Refunded”, “refunded”]]
為了讓不同的model可以任意擴充使用這樣的模組,這邊運用了一點metaprogramming的技巧,寫了一個EnumValueConcern,程式碼像下面這樣
有需要的model就可以直接include EnumValueConcern,像Order model這樣
include之後,Order model就可以對status跟shipment_mode這兩個屬性使用enum_options_for_select這個方法了,直接從rails console看看結果
兩個屬性都可以組出我們預期的格式
再來試一個Product model
在rails console試試看使用enum_options_for_select
結果也是跟預期一樣,把category定義的enum跟human_attribute_name轉換的結果都印出來了,果然寫一個module EnumValueConcern讓model共用真的是相當方便啊!