Tìm hiểu về Polymorphic Associations trong Ruby on Rails

Polymorphic associations có thể khó hiểu và nhiều lần chúng ta có thể nhầm lẫn về việc khi nào nên sử dụng, cách sử dụng và nó khác với các association khác như thế nào. Trong bài viết này, chúng ta sẽ đề cập đến những vấn đề này.

Giới thiệu

Active Record Associations là một trong những tính năng quan trọng nhất của Rails. Polymorphic association là một phần của các associations này. Ruby on Rails guide tuyên bố “với các polymorphic associations, một model có thể thuộc về nhiều hơn một model khác, trên một association duy nhất.

Cơ bản

Trong khi viết một số ứng dụng rails, bạn sẽ gặp phải tình huống khi bạn có các model associations có vẻ giống nhau, ví dụ, giả sử bạn có các model Course (Khoá học) và Lab (Phòng thí nghiệm) trong ứng dụng của mình. Bây giờ mỗi CourseLab cần TeachingAssistants (Trợ lí giảng dạy), vì vậy bạn cần liên kết CourseLab với các TeachingAssistants tương ứng của chúng. Nếu bạn sử dụng liên kết has_many/belongs_to ở đây, bạn sẽ có hai model tương tự ví dụ như là TeachingAssistantsCourseTeachingAssisstantsLab chẳng hạn. Thay vì có hai model khác nhau, bạn có thể có một model duy nhất, đó là TeachingAssistant và bạn có thể liên kết model này với các model CourseLab bằng cách sử dụng polymorphic association

Hãy xem cách thực hiện nó. Đầu tiên chúng ta sẽ cần tạo model TeachingAssistant.

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

Ở đây ta_duty_id là một khóa ngoại và ta_duty_type sẽ cho biết model nào sẽ liên kết với model TeachingAssistant

/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

Bây giờ model của chúng ta đã được tạo, hãy chạy migrationrails db:migrate. Bây giờ, hãy thiết lập polymorphic association model TeachingAssistant

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

Bằng cách làm cho TeachingAssistant thuộc về ta_duty thay vì bất kỳ model khác, chúng tôi đã khai báo polymorphic association bằng từ khoá polymorphic: true . Lưu ý rằng chúng ta không có bất kỳ model /class ta_duty nào trong ứng dụng, ta_duty chỉ quan tâm đến polymorphic association. Bây giờ hãy nhìn vào mặt khác.

/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

Đoạn code ở trên nói rằng CourseLab có nhiều TeachingAssistant thông qua polymorphic association ta_duty. Hãy xem diễn tả của polymorphic association mà chúng tôi đang thiết lập thông qua hình ảnh ở dưới. Chúng ta có thể thấy model TeachingAssistant được liên kết với hai model CourseLab thông qua ta_duty.

Bây giờ hãy kiểm tra điều này trongRails console. Nhập rails c và làm theo các lệnh.

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

Chúng tôi đã Course và tạo liên kết giữa CourseTeachingAssistant thông qua polymorphic association ta_duty. Trong dòng sáu, chúng tôi đã thử nghiệm nếu association hoạt động.

STI vs Polymorphic Association

Single Table Inheritance (STI) thường được so sánh với các polymorphic associations. Nó có thể thay thế tốt cho các polymorphic associations (tùy thuộc vào tình huống). Trong STI, bạn thừa hưởng các model tương tự từ model cơ sở kế thừa từ ActiveRecord::Base, ví dụ trong trường hợp này

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

Từ hình dưới đây, bạn sẽ có được hình ảnh rõ ràng về single table inheritance khác với polymorphic associations và xem STI trông như thế nào.

Như chúng ta có thể thấy STI cung cấp cho bạn sự linh hoạt hơn so với polymorphic association nhưng chúng ta phải tạo các class riêng biệt để thực hiện các association. STI thêm nhiều code hơn và có thể khó thực hiện so với polymorphic association. Mặc dù bạn nên đưa ra quyết định cuối cùng tùy thuộc vào trường hợp sử dụng. Trong trường hợp của chúng ta, polymorphic association rõ ràng là một lựa chọn tốt hơn STI.

Kết hợp has_many :through và polymorphic association

Có thể có những trường hợp đặc biệt mà bạn cần has_many :through qua polymorphic association. Mặc dù không khó hiểu/thực hiện, lần đầu tiên mọi người có thể bị nhầm lẫn. Ví dụ Professor (Giáo sư) muốn biết những Course/Lab nào được giao cho TeachingAssistant của mình. Điều này có thể được thực hiện bằng cách làm 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

Chúng ta đã kết hợp has_many :through và polymorphic association. Để kiểm tra, bạn có thể thực hiện các truy vấn như Professor.first.course_tas. Lưu ý rằng chúng ta đã thêm liên kết giữa ProfessorTeachingAssistant, vì vậy hãy đảm bảo bạn thực hiện các thay đổi tương ứng.

Hạn chế

Khi thêm Bi-directional Associations trong ứng dụng rails, người ta sử dụng inverse_of: được cung cấp bởi Active Record.inverse_of: không hoạt động với polymorphic association.

Kết luận

Mình đã trình bày những điều cơ bản và thực hiện polymorphic association trong Rails, so sánh nó với các associations khác và xem làm thế nào nó có thể được mở rộng với các associations khác. Một khi bạn hiểu cách sử dụng chính xác và phù hợp của polymorphic association, bạn có thể làm cho ứng dụng của mình hiệu quả hơn.

Tham khảo: https://launchschool.com/blog/understanding-polymorphic-associations-in-rails