Tìm hiểu Polymorphic Associations trong Rails

Polymorphic (đa hình) là 1 khái niệm tương đối khó hiểu và dễ bị lẫn lộn khi sử dụng. Vậy sao nào để ta có thể hiểu và sử dụng đúng, qua bài viết này tôi sẽ giúp các bạn làm sáng tỏ

1. Giới thiệu

Active Record Associations là một trong những chức năng quan trọng nhất của Rails và Polymorphic Association là một phần trong đó. Với Polymorphic Association một model có thể tạo quan hệ với nhiều hơn một model khác mà chỉ thông qua một association.

2. Cài đặt

Khi viết một ứng dụng Rails đôi khi tao gặp phải trường hợp một model có nhiều liên kết có ý nghĩa giống nhau. Ví dụ, chúng ta có 2 model là CourseLab. Với mỗi Course hay Lab chúng ta có nhiều TeachingAssistant. Nếu sử dụng has_many/belongs_to ở đây bạn phải hai liên kết tương ứng giữa Course với TeachingAssistantLab với TeachingAssistan. Thay vì đó, chúng ta có thể sử dụng Polymorphic Association để tạo một liên kết tới đồng thời tới hai model CourseLab từ TeachingAssistant.

Trước tiên chúng ta tạo các model bằng cách chạy :

TeachingAssistant

rails g model TeachingAssistant name:string ta_duty_id:integer ta_duty_type:string

Course

rails g model Course name:string

Lab

rails g model Lab name:string

ta_duty_id : foreign key ta_duty_type: quyết định xem TeachingAssistant liên kết với model nào (Course hay Lab)

/db/migrate/********_create_teaching_assistants.rb

class CreateTeachingAssistants < ActiveRecord::Migration
  def change
    create_table :teaching_assistants do |t|
      t.string :name
      t.integer :ta_duty_id
      t.string :ta_duty_type

      t.timestamps
    end
  end
end

Chạy rake db:migrate. Giờ chúng ta đã có model, việc tiếp theo là khai báo Polymorphic Association cho TeachingAssistant

class TeachingAssistant < ActiveRecord::Base
  belongs_to :ta_duty, polymorphic: true
end

Bằng việc khai báo TeachingAssistan belongs_to ta_duty thay vì Course hay Lab chúng ta đã tạo một Polymorphic Association.

Chú ý : ta_duty không phải 1 model hay class trong hệ thống mà chỉ là tên đại diện cho Polymorphic Association.

Tiếp theo là 2 model CourseLab

/app/models/course.rb

class Course < ActiveRecord::Base
  has_many :teaching_assistants, as: :ta_duty
end

/app/models/lab.rb

class Lab < ActiveRecord::Base
  has_many :teaching_assistants, as: :ta_duty
end

Với khai báo ở trên tao có thể hiểu rằng CourseLab has_many TeachingAssistant thông qua polymorphic association ta_duty. Sơ đồ dưới sẽ giúp các bạn dễ hình dung hơn về mối quan hệ trên.

polymorphic.png

Giờ hãy vào console tạo một TeachingAssistant để check nhé:

001 > ta = TeachingAssistant.create name: "ta_name"
002 > c = Course.create name: "course_name"
003 > ta.update_attribute(:ta_duty, c)
 => true
004 > Course.last.teaching_assistants.last.name
 => "ta_name"
2.0.0-p247 :005 >

3. STI vs Polymorphic Association

Single Table Inheritance(STI) thường được so sánh với Polymorphic Association. Đó cũng là một lựa chọn tương đối tốt so với Polymorphic Association tùy vào từng trường hợp. STI sử dụng cơ chế thừa kế tương tự như ActiveRecord::Base. Ví dụ :

class TeachingAssistant < ActiveRecord::Base
class CourseTa < TeachingAssistant
class LabTa < TeachingAssistant

Trong trường hợp này ta sẽ có một sơ đồ quan hệ khác với Polymorphic Association

sti.png

STI yêu cầu bạn phải tạo ra các class tương ứng để khai báo quan hệ. Bạn phải viết nhiều dòng code hơn và phức tạp hơn sơ với Polymorphic Association

4. Kết hợp has_many :through và Polymorphic Association

Có thể có trường hợp bạn cần phải kết hợp has_many :through với Polymorphic Assoctiation. Mặc dù không khó để khai báo nhưng trường hợp này rất hay bị nhầm lẫn. Sau đây ta hay xét một ví dụ. Giáo sư (Professor) muốn biết Course hay Lab nào có TeachingAssistant của mình. Ta có thẻ khai báo như sau:

/app/models/professor.rb

class Professor < ActiveRecord::Base
  has_many :teaching_assistants
  has_many :course_tas, through: :teaching_assistants, source: :ta_duty, source_type: "Course"
  has_many :lab_tas, through: :teaching_assistants, source: :ta_duty, source_type: "Lab"
end

/app/models/teaching_assistant.rb

class TeachingAssistant < ActiveRecord::Base
  belongs_to :professors
  belongs_to :ta_duty, polymorphic: true
end

5. Kết luận

Trên đây là những điều cơ bản và cách khai báo Polymorphic Association trong Rails. Ngoài ra tôi còn giúp các bạn so sánh nó với STI và cách thức kết hợp nó với một quan hệ khác. Rất mong các bạn có thể hiểu rõ và vận dụng thật tốt vào những project của mình.

Tham khảo thêm

Ruby on Rails Guide

Ruby on Rails API

All Rights Reserved