Polymorphic association
Bài đăng này đã không được cập nhật trong 8 năm
Polymorphic association Tuy không phải là một feauture mới của Rails, tuy nhiên vẫn có nhiều developer RoR hiểu nhầm, mập mờ về polymorphic association, vì polymorphic vẫn có vẻ hơi “lạ” , hoặc vẫn chưa hiểu polymorphic là gì nên hay bị nhầm lẫn, khó khăn khi sử dụng.
Bài chia sẻ lần này, tôi sẽ làm rõ một số vấn đề hay bị hiểu nhầm về polymorphic, mong muốn giúp các bạn không còn lúng túng khi “phải đụng đến” polymorphic association.
Hai câu hỏi thường gặp nhất khi đối mặt với vấn đề ( ở đây là polymorphic association) đó là: nó là gì? Nó hoạt động như thế nào?
Vậy polymorphic association trong Rails là gì? Nó được định nghĩa ở Polymorphic. Điều rút ra là, polymorphic là một phần xử lý có phần nâng cao hơn giữa các association models, một model có thể belongs_to với một hoặc nhiều hơn models khác, mà chỉ cần định nghĩa một association.
Sau đây, chúng ta sẽ định nghĩa polymorphic:
Tạo ra bảng email_questions với mục đích lưu email người hỏi, và nội dung hỏi.
Bảng lawyers
, engineers
là người có thể hỏi.
Theo cách thông thường ta sẽ làm như sau:
Class CreateEmailQuestions < ActiveRecord::Migration[5.0]
def change
create_table :email_questions do |t|
t.string : questioner _email
t.text :content
t.references :lawyer
t.references :engineer
t.timestamps
end
end
end
Tạo ra bảng lawyers
class CreateLawyer < ActiveRecord::Migration[5.0]
def change
create_table :lawyers do |t|
t.string :name
t.string :email
t.integer :year_old
end
end
end
Tạo ra bảng engineers
class CreateEngineers < ActiveRecord::Migration[5.0]
def change
create_table :engineers do |t|
t.string :name
t.string :email
t.string :city
end
end
end
Xây dựng association giữa email_questions
và hai bảng engineers
và lawyers
:
class EmailQuestion < ApplicationRecord
belongs_to :lawyers
belongs_to :engineer
end
class Lawyer < ApplicationRecord
has_many :email_questions
end
class Engineer < ApplicationRecord
has_many :email_questions
end
Bây giờ, ta có một biến instance @email_question của class EmailQuestion, muốn tìm người đã được hỏi của @email_question này thì ta có thể thử
@email_question.lawyer || @email_question.engineer
Nhìn qua có vẻ khá rắc rối và dài dòng.
Đây là lúc, chúng ta nên áp dụng polymorphic
để giảm thiểu khai báo thừa:
Ví dụ: email_questions
references đến lawyers
và engineers
Polymorphic được định nghĩa như sau
Với bảng email_questions:
Class CreateEmailQuestions < ActiveRecord::Migration[5.0]
def change
create_table :email_questions do |t|
t.string : questioner _email
t.text :content
t.string :questionable_type
t.integer :questionable_id
t.timestamps
end
end
end
Ở đây, questionable_type
là tên class questionable(“Lawyer” or “Engineer”)
questionable_id
là id của questionable (yaoming).
Từ questionable_type và questionable_id của object email_question thì ta có thể tìm được questionable(người được hỏi của email_question), đồng thời, hoàn thành các khai báo polymorphic trong model.
Với các model:
Xây dựng association giữa email_questions và hai bảng engineers và lawyers:
class EmailQuestion < ApplicationRecord
belongs_to :questionable, polymorphic: true
end
class Lawyer < ApplicationRecord
has_many :email_questions, as: questionable
end
class Engineer < ApplicationRecord
has_many :email_questions, as: questionable
end
Thậm chí, ở hai model lawyer và engineer chúng ta còn có thể đưa vào module dùng chung (trong concern model)
module QuestionableRelation
extend ActiveSupport::Concern
included do
has_many :email_questions, as: questionable
end
end
Và ta chỉ cần khai báo
class Lawyer < ApplicationRecord
include QuestionableRelation
end
class Engineer < ApplicationRecord
include QuestionableRelation
end
=> DRY
Đừng quên rake db:migrate
khi tạo/sửa table.
Chúng ta có thể kiểm tra các association của các model bằng cách dùng
reflect_on_all_associations
(xem thêm)
Bây giờ, ta có thể dễ dàng hơn trong việc tìm questionable:
@ questionable = @email_question.questionable
Nếu muốn test với Rspec (ở đây sử dụng shoulda-matchers )
Rspec model/email_question_spec:
it {should belong_to(:questionable)}
Rspec model/lawyer_spec:
it {should have_many(:email_questions)}
Hoặc có thể dùng behaves_like để test cho module.
Trên đây là những ý kiến chia sẻ của tôi về cách dùng và hoạt động của polymorphic association trong Rails
tài liệu tham khảo:
All rights reserved