Polymorphic association
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 ...
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:
Railscast
Rspec
guides RoR