October 10, 2023

Viiisit [Ruby on Rails] - Active Record Query - Enum!

#ruby on rails#active record#enum

在 Active Record 裡,有一個滿常見的功能,Enum,這功能該如何使用,今天就來點 Enum 吧!

Enum 是什麼?

Enum(Enumeration的縮寫)稱為列舉,是一種程式設計中常見的資料型別,
用於定義一組具有固定名稱的整數常數。
這些名稱通常用作代表特定狀態、選項、或類別的符號,使我們在程式上更易於閱讀、理解和維護。

讓我們來看看 Rails 裡該如何使用 Enum 吧!

Enum in Ruby on Rails

在 Rails 中,Enum(列舉)是一個用於定義 Model 屬性的機制。
可以使用 Enum 來將整數映射到易於理解的名稱(自定義名稱),以增強代碼的可讀性。

首先,我們已經建立好 Order Model,
現在我想針對一張訂單可能會有的狀態新增一個欄位到 orders 資料表上。
一張訂單可能有這四種狀態:pendingshippeddeliveredcanceled

可以在建立狀態欄位上使用整數的資料型態來設置:

1
rails g migration AddStatusToOrder status:integer

點進去剛剛新增的 migration 檔案:

1
2
3
4
5
class AddStatusToOrder < ActiveRecord::Migration[7.0]
def change
add_column :orders, :status, :integer
end
end

如果想設定預設值為 0,可以加上 default: 0

1
2
3
4
5
class AddStatusToOrder < ActiveRecord::Migration[7.0]
def change
add_column :orders, :status, :integer, default: 0
end
end

如何在 Model 定義 Enum?

接著,我們到 Order Model,使用 Enum 來定義狀態:

在定義上可以使用多種方式,來看看有哪些寫法吧!

Enum 在 Rails 如使用?

在 Model 定義好 Enum 之後,我們要如何使用呢?

Order Model 擁有新的方法了!

透過複數型來抓取整個定義好的狀態 - 使用 statuses 方法

透過剛剛的 Hash 定義,我們便可以用 Order.statuses 得到一個 Hash 如下:

1
2
3
4
5
6
7
8
9
# app/models/order.rb
class Order < ApplicationRecord
enum :status, {
pending: 0,
shipped: 1,
delivered: 2,
canceled: 3
}
end
1
2
3
4
5
Order.statuses
=> {"pending"=>0, "shipped"=>1, "delivered"=>2, "canceled"=>3}

Order.statuses[:pending] or Order.statuses["pending"]
=> 0

查詢具有相應狀態的訂單記錄 - 使用 pending, shipped, delivered, canceled 方法

透過 Order.pending, Order.shipped, Order.delivered, Order.canceled
這些方法用於查詢具有相應狀態的訂單記錄。
Order.pending 將返回所有狀態為 “pending” 的訂單。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Order.pending
Order Load (0.3ms) SELECT "orders".* FROM "orders" WHERE "orders"."status" = ? [["status", 0]]
=>
[#<Order:0x0000000110836420
id: 3,
order_name: "20231010001",
created_at: Tue, 10 Oct 2023 06:17:08.953478000 UTC +00:00,
updated_at: Tue, 10 Oct 2023 06:17:08.953478000 UTC +00:00,
status: "pending">,
#<Order:0x00000001108362e0
id: 4,
order_name: "20231010002",
created_at: Tue, 10 Oct 2023 06:17:17.280539000 UTC +00:00,
updated_at: Tue, 10 Oct 2023 06:17:17.280539000 UTC +00:00,
status: "pending">,
#<Order:0x00000001108361a0
id: 5,
order_name: "20231010001",
created_at: Tue, 10 Oct 2023 07:12:39.204159000 UTC +00:00,
updated_at: Tue, 10 Oct 2023 07:12:39.204159000 UTC +00:00,
status: "pending">]

透過建立一張新的 order - 使用 status 方法

status 方法是一個可以用在知道一個實體變數的狀態方法:
建立一張新的 order001 之後,可以用 order001.status
剛剛因為有設定 default: 0,所以結果會是 “pending”。

1
2
3
order001 = Order.create(order_name: "20231010001")
order001.status
=> "pending"

確認 order 狀態 - 使用 pending?, shipped?, delivered?, canceled? 方法

透過自定義的名稱加上 ?,可以檢查是否為某種狀態:

1
2
3
4
5
6
7
8
9
order001 = Order.create(order_name: "20231010001")
order001.pending?
=> true
order001.shipped?
=> false
order001.delivered?
=> false
order001.canceled?
=> false

更新 order 狀態 - 使用 pending!, shipped!, delivered!, canceled! 方法

透過自定義的名稱加上 !,可以更新狀態:

1
2
3
4
5
6
order001 = Order.create(order_name: "20231010001")
order001.shipped!
TRANSACTION (0.1ms) begin transaction
Order Update (1.4ms) UPDATE "orders" SET "updated_at" = ?, "status" = ? WHERE "orders"."id" = ? [["updated_at", "2023-10-10 07:36:12.025293"], ["status", 1], ["id", 3]]
TRANSACTION (1.2ms) commit transaction
=> true
1
2
3
4
order001.shipped?
=> true
order001.pending?
=> false

另外,我們也可以使用:order001.update(status: :shipped)

1
2
3
4
5
order001.update(status: :shipped)
TRANSACTION (0.1ms) begin transaction
Order Update (0.8ms) UPDATE "orders" SET "updated_at" = ?, "status" = ? WHERE "orders"."id" = ? [["updated_at", "2023-10-10 07:38:30.268312"], ["status", 1], ["id", 3]]
TRANSACTION (1.7ms) commit transaction
=> true
1
2
3
4
order001.shipped?
=> true
order001.pending?
=> false

但是相較 !,這寫法比較冗長,我比較想要快速一點點,通常我都會用第一種方式去更新!

prefix, suffix

在 Enum 還可以使用 prefixsuffix 選項來控制生成的方法的前綴和後綴,以避免命名衝突。

今天就到這啦!我們下篇見~


參考資料:
Active Record Query Interface #Enums
How to Use Enums in Rails
How to use enum attributes in Ruby on Rails