在實際開發中,經常會遇到資料表內部記錄之間需要互相關聯的情況。典型例子是公司員工架構:每位員工都有直屬主管,同時也可能擔任他人的導師,但他們本身同樣是員工記錄。在 Rails 中,透過 belongs_to 與 has_many 搭配 :class_name 與 :foreign_key 選項,即可輕鬆建模這類自關聯關係。
class Employee < ActiveRecord::Base belongs_to :manager, :class_name => "Employee", :foreign_key => "manager_id" belongs_to :mentor, :class_name => "Employee", :foreign_key => "mentor_id" has_many :mentored_employees, :class_name => "Employee", :foreign_key => "mentor_id" has_many :managed_employees, :class_name => "Employee", :foreign_key => "manager_id" end
上述程式碼在同一個 employees 資料表上建立了四條關聯路徑:
manager / managed_employees:直屬上下級關係mentor / mentored_employees:導師與受導者關係Employee.delete_all adam = Employee.create(:id => 1, :name => "Adam") beth = Employee.create(:id => 2, :name => "Beth") clem = Employee.new(:name => "Clem") clem.manager = adam clem.mentor = beth clem.save! dawn = Employee.new(:name => "Dawn") dawn.manager = adam dawn.mentor = clem dawn.save!
此範例建立了四位員工:Adam 與 Beth 為頂層,Clem 向 Adam 回報並由 Beth 指導,Dawn 同樣向 Adam 回報但由 Clem 指導。
# Adam 直屬下屬
p adam.managed_employees.map {|e| e.name} # => ["Clem", "Dawn"]
# Adam 指導的員工
p adam.mentored_employees # => []
# Dawn 的導師
p dawn.mentor.name # => "Clem"
透過自關聯,我們可以用直觀的物件導向語法,輕鬆回答「X 的下屬是誰?」、「Y 的上級是誰?」等問題,無需撰寫複雜的 SQL 連結查詢。