Các liên kết đa hình trong Rails

Tram Ho

Ai làm việc với Rails thì chắc hẳn đã nghe đến Polymorphic Association – một giải pháp khá hay để xây dựng các quan hệ. Trong bài viết này sẽ đề cập đến các vấn đề về Polymorphic Association

I. Khái niệm

Polymorphic Associations là một loại Active Record Association, giúp tạo liên kết giữa 1 model với nhiều model khác thông qua 1 association duy nhất.
Nếu không dùng polymorphic chúng ta sẽ cài đặt như ảnh dưới.


Bình thường, để tìm ra chủ sở hữu một Picture, chúng ta nhìn cột foreign_key. Foreign_key là id được sử dụng để tìm kiếm bản ghi phù hợp trong bảng của model liên quan. Tuy nhiên, Picture có hai khóa ngoại: employee_id và product_id. Vấn đề là ở đây, khi thực hiện tìm chủ sở hữu Picture, sẽ phải thực hiện kiểm tra cả hai cột để tìm đúng khóa ngoại, thay vì một cột. Điều gì xảy ra nếu gặp phải tình huống mà cả hai cột đều hợp lệ? Ngoài ra trong trường hợp này chỉ có hai bảng là Employee và Product nếu có nhiều bảng hơn thế số lượng foreign_key mà Picture cần phải chứa là rất lớn.
Polymorphic Association giải quyết vấn đề này bằng cách gộp chức năng thành một liên kết duy nhất.
Ví dụ, chúng ta có 2 model là Employee và Product. Với mỗi Employee hay Product chúng ta có nhiều Pictures . Nếu sử dụng has_many/belongs_to ở đây bạn phải hai liên kết tương ứng giữa Employee với Pictures và Product với Pictures. 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 Employee và Pictures từ Pictures. Chúng ta đến với phần cài đặt để hiểu rõ hơn về giải pháp này.

II. Cài đặt

Để cơ sở dữ liệu biết chúng ta sử dụng Polymorphic Associations đầu tiên cần khai báo cột foreign key và cột type trong bảng Picture.

Trong đó :

  • imageable_id: foreign key
  • imageable_type: dùng để quyết định xem Picture liên kết với model nào (Employee hay Product)

hoặc có thể làm đơn giản hơn như:

Theo quy ước, Rails đặt tên một liên kết đa hình là tên class kết hợp với đuôi able.
Điều này thể hiện rõ ràng trong mối quan hệ rằng nó là một class đa hình. Nhưng cũng có thể sử dụng bất kỳ tên nào cho liên kết đa hình, ví dụ ở đây dùng :imageablechứ không phải :pictureable cho class Picture.

Việc tiếp theo là khai báo Polymorphic Association cho Picture

Bằng việc khai báo Picture belongs_to  imageable thay vì Employee hay Product chúng ta đã tạo một Polymorphic Association.
Chú ý : imageable 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 Picture và Product

Với khai báo ở trên ta có thể hiểu rằng Employee và Product has_many Picture thông qua polymorphic association imageable.

Khi đó các quan hệ của chúng ta sẽ có dạng như :

Một trường hợp đặc biệt khác, khi ứng dụng polymorphic vào làm comment. Chúng ta xét bài toán một Room có nhiều comment và một comment có nhiều comment con. Chúng ta sẽ cài đặt như sau: 

III. Ưu nhược điểm

Liên kết belongs_to bình thường, chúng ta sử dụng các khóa ngoại để tham chiếu
Polymorphic không thể có các khóa ngoại, sử dụng type và id thay khóa ngoại.

Trong một liên kết belongs_to bình thường, chúng ta sử dụng các khóa ngoại để tham chiếu liên kết. Thực sự chúng có nhiều quyền hơn là việc chỉ tạo một liên kết. Các khóa ngoại cũng ngăn các lỗi tham chiếu bằng cách yêu cầu các đối tượng được tham chiếu tới phải tồn tại trong table. Xuất hiện lỗi khi tham chiếu tới đối tượng null. 
Mà các class Polymorphic lại không thể có các khóa ngoại, sử dụng type và id thay khóa ngoại. Điều này có nghĩa là chúng ta mất đi sự bảo vệ của các khóa ngoại. Nếu ai đó có quyền truy cập trực tiếp vào cơ sở dữ liệu thì đều có thể tạo hoặc cập nhật các đối tượng tham chiếu đến các đối tượng null.

Ví dụ: Comment vẫn được tạo ngay cả khi Room được liên kết không tồn tại:

Điểm mạnh

  • Dễ dàng mở rộng quy mô dữ liệu: dữ liệu được phân phối trên một số bảng khác nhau để giảm thiểu tối đa bảng.
  • Dễ dàng mở rộng số lượng model: nhiều model hơn có thể dễ dàng kết hợp với các class đa hình.
  • DRY: tạo một class sử dụng cho nhiều class khác.

Điểm yếu

  • Không thể có khóa ngoại. Cột id có thể tham chiếu bất kỳ tới model table nào được liên kết, điều này làm chậm truy vấn. Nó phải kết hợp với cột type.
  • Nếu bảng lớn, cần nhiều không gian lưu trữ các giá trị chuỗi cho imageable_type.
  • Toàn vẹn dữ liệu bị xâm phạm.

IV. Single table inheritance hay polymorphic associations ?

Một tình huống thường xảy ra khi một số model này cần có quyền truy cập vào chức năng của một model thứ ba nào đó.

  • Rails có hỗ trợ hai phương thức để chúng ta “ứng phó” với những trường hợp này là: single table inheritance và polymorphic associations

Single table inheritance(STI)

STI thích hợp sử dụng khi các model chia sẻ dữ liệu hay trạng thái cho nhau. Nhiều subclass cùng kế thừa từ một superclass.

Một cửa hàng phân phối các loại xe: Cars, Motorcycles, Bicycles. Quản lý muốn theo dõi thông tin của từng xe, bao gồm: màu sắc xe (color), giá bán (price), tình trạng bán (purchased).
Đây là một tình huống hoàn hảo cho việc dùng STI, những dữ liệu giống nhau sẽ được cung cấp cho từng class.
Tạo một superclass Vehicle với các thuộc tính color, price và purchased. Khi đó, các class con kế thừa từ nó có thể get tất cả các thuộc tính đó.

Vậy chọn STI hay Polymorphic Associations?

Dựa vào bốn yếu tố cần xem xét khi quyết định lựa chọn một trong hai phương pháp này liệu rằng nó có phù hợp với nhu cầu của bạn hay không?

V. Tài liệu tham khảo

https://www.freecodecamp.org/news/single-table-inheritance-vs-polymorphic-associations-in-rails-af3a07a204f2/
https://guides.rubyonrails.org/association_basics.html#polymorphic-ass

Chia sẻ bài viết ngay

Nguồn bài viết : Viblo