October 5, 2023

Viiisit [Ruby on Rails] - Active Record Associations (下)!

#ruby on rails#active record

上回我們講到 Active Record Association 的基礎觀念,今天繼續延伸下去!

Polymorphic Association 多型關聯

多型關聯(Polymorphic Association)是一種在資料庫中建立關聯的方法,允許一個模型與多個其他模型建立關聯,並且可以根據需要動態指向這些模型中的一個。主要目的是在不知道或不確定關聯到哪個模型的情況下實現關聯。

想像你正在建立一個簡單的網站,用戶可以發表評論(Comments),但這些評論可以關聯到多種不同類型的內容,例如文章(Articles)和圖片(Photos)。這時就可以使用多型關聯。

我們就可以在 Comment 模型中使用 commentable 來關聯到不同類型的內容。
例如,當用戶發表一個評論時:

1
2
3
4
5
6
7
# 建立一個評論關聯到一篇文章
article = Article.find(1)
comment = article.comments.create(content: '這是一個評論')

# 建立一個評論關聯到一張圖片
photo = Photo.find(2)
comment = photo.comments.create(content: '這是另一個評論')

可以通過 commentable 關聯動態地關聯到不同類型的內容,同時保持代碼的簡單性和可擴展性。

Self-joining Associations 自連接關聯

自連接(Self-joining)關聯是一種在資料庫中建立關聯的技術,其中同一個表格(或模型)中的記錄可以與該表格中的其他記錄建立關聯。換句話說,就是在一個模型中創建一個與自己相關聯的關聯,通常用於處理與模型自身相關的數據。

例如,有一個 “Employee” 的模型,每個員工都有一個直接上級(即另一個員工),並且他們都屬於同一個公司。
可以使用自連接來建立員工之間的關聯,同時維護每個員工的直接上級。

1
2
3
4
5
# Employee 模型
class Employee < ApplicationRecord
belongs_to :manager, class_name: 'Employee', optional: true
has_many :subordinates, class_name: 'Employee', foreign_key: 'manager_id'
end

這個模型有兩個關聯:

  1. belongs_to :manager:這個關聯表示每個員工都屬於另一個員工,並且 class_name 參數指定了關聯的模型是自身的 “Employee” 模型。意即,每個員工都有一個直接上級,但不是每個員工都必須有一個直接上級(所以我們使用 optional: true)。

  2. has_many :subordinates:這個關聯表示每個員工都可以擁有多個下屬,同樣,我們使用 class_nameforeign_key 參數指定了關聯的模型和外鍵字段。

使用這些關聯來建立員工之間的關係:

1
2
3
4
5
6
7
8
# 建立員工記錄
manager = Employee.create(name: 'Manager')
employee1 = Employee.create(name: 'Employee 1', manager: manager)
employee2 = Employee.create(name: 'Employee 2', manager: manager)

# 查詢員工的直接上級和下屬
puts employee1.manager.name # 輸出 "Manager"
puts manager.subordinates.pluck(:name) # 輸出 ["Employee 1", "Employee 2"]

這樣,你可以使用自連接來建立模型內部的層次結構關係,這在處理組織結構、層次性數據或任何需要記錄與自身建立關聯的情況下非常有用。自連接使用相同的模型在內部建立關聯,同時保持代碼的簡潔性和可讀性。

補充觀念

在 Rails Guide - Active Record Association 中,有提及關於在 Rails 應用程式中有效使用 Active Record Association 的注意事項:

  1. 控制緩存(Controlling caching)

    Active Record 通常會對關聯數據進行緩存,以提高性能。
    一些查詢結果可能會被緩存在記憶體中,但有時這可能導致意外的行為。
    可以使用 reload 方法來重新加載關聯數據,以確保獲取的是最新的數據。

  2. 避免名稱衝突(Avoiding name collisions)

    在模型中定義多個關聯時,要注意避免名稱衝突!
    如果多個關聯使用相同的名稱,可能會導致混淆和錯誤。
    為每個關聯選擇具有描述性的名稱,並使用 :class_name 選項來指定模型的名稱,以避免混淆。

  3. 更新模式(Updating the schema)

    更改模型之間的關聯時,記得更新 schema!
    如果新增、刪除或修改關聯,需要遷移(migration)來更新資料庫結構,以反映新的關聯。

  4. 控制關聯範圍(Controlling association scope)

    使用 has_manyhas_one 關聯時,可以使用 :scope 選項來定義範圍,以限制關聯中的數據。這對於過濾或排序關聯數據非常有用。

  5. 雙向關聯(Bi-directional associations)

    有時可能需要在兩個模型之間建立雙向關聯,也就是說,當一個模型關聯到另一個模型時,也希望反向的關聯也存在。這可以通過在兩個模型中分別定義關聯來實現。確保處理好雙向關聯可以更方便地訪問和操作數據。


參考資料:
Polymorphic Association
PJCHENder - [Rails] Active Record Association (Model)