天天看點

譯:使用ActiveRecord Enums建立簡單易讀的屬性

 Creating Easy, Readable Attributes With ActiveRecord Enums

設想一個問題的狀态可能為“暫停”,“通過”或“标注”。或者一個電話号碼可能是“家庭号碼”,“辦公号碼”,“手機号碼”或者“傳真号碼”(1982年的話)

有些子產品需要這種類型的資料:隻對應少許值的屬性,并且這些值幾乎永遠不會改變。

如果使用純Ruby的話,可以通過使用

symbol

來解決這個問題。

可以建立

PhoneNumberType

或者

QuestionStatus

子產品,并通過定義

belongs_to

關系來關聯這些值,但是這麼簡單的需求似乎并不值得這麼做,因為僅僅将這些值放在

yaml

檔案中一樣可以滿足需求。

現在我們來看一個解決這類需求的真正利器:在Rails4.1中引入的 ActiveRecord enums。

Model中的少量值

ActiveRecord enums用法很簡單,你可以為model建立一個整數類型的列:

bin/rails g model phone number:string phone_number_type:integer
           

列出這個屬性可能的值

class Phone < ActiveRecord::Base
  enum phone_number_type: [:home, :office, :mobile, :fax]
end
 ```
現在你可以直接操作字元串而不是數字了。
從前:
```ruby
irb(main)::> Phone.first.phone_number_type
=> 




<div class="se-preview-section-delimiter"></div>
           

采用ActiveRecord enums後:

irb(main)::> Phone.first.phone_number_type
=> "fax"




<div class="se-preview-section-delimiter"></div>
           

通過字元串或者數字都可以改變屬性的值

irb(main)::> phone.phone_number_type = ; phone.phone_number_type
=> "office"
irb(main)::> phone.phone_number_type = "mobile"; phone.phone_number_type
=> "mobile"




<div class="se-preview-section-delimiter"></div>
           

甚至是通過感歎方法

irb(main)::> phone.office!
=> true
irb(main)::> phone.phone_number_type
=> "office"




<div class="se-preview-section-delimiter"></div>
           

檢視屬性是否支援某些值:

irb(main)::> phone.office?
=> true




<div class="se-preview-section-delimiter"></div>
           

查詢滿足屬性值的所有對象:

irb(main):8:> Phone.office
  Phone Load (.ms)  SELECT "phones".* FROM "phones" WHERE "phones"."phone_number_type" = ?  [["phone_number_type", ]]




<div class="se-preview-section-delimiter"></div>
           

檢視屬性所有可用值:

irb(main):9:> Phone.phone_number_types
=> {"home"=>, "office"=>, "mobile"=>, "fax"=>}




<div class="se-preview-section-delimiter"></div>
           

在HTML表單中也可以友善使用:

<div class="field">
  <%= f.label :phone_number_type %><br>
  <%= f.select :phone_number_type, Phone.phone_number_types.keys %>
</div>




<div class="se-preview-section-delimiter"></div>
           
譯:使用ActiveRecord Enums建立簡單易讀的屬性

需要注意的是

Enums

并不是沒有缺陷,如果不想在日後陷入麻煩的話,這裡有幾個問題需要注意。

定義enum時,注意順序。假設你突然決定使用字母順序來排列enum值:

class Phone < ActiveRecord::Base
   enum phone_number_type: [:fax, :home, :mobile, :office]
end




<div class="se-preview-section-delimiter"></div>
           

那麼你電話号碼的類型比對就有問題咯。可以通過設定序号值來解決這個問題:

class Phone < ActiveRecord::Base
   enum phone_number_type: {fax: , home: , mobile: , office: }
end
           

但是講真,最好的選擇是不要改變值的順序。

另一個更大的問題存在于Rails之外。盡管Rails将這些enum值當作字元串處理,而在資料庫中它們隻是一些數字。其他人看原始資料時并不知道這些數字所代表的含義,這就意味着讀取這個資料庫在所有應用都要有一份enum值的映射。

碰到這樣需求的時候可以選擇将enum映射存入資料庫或yaml檔案中。但這不符合

DRY

原則,因為現在你在兩個地方定義了enum。而且随着應用規模的擴充,可能我們一開始想要避免的做法反而更好:建立另一個模型和關聯,即

Phone

belong_to

PhoneNumberType

但是如果想保持簡單,

enums

依然是不錯的選擇。