Tìm hiểu Single Table Inheritance (STI) trong Rails
1. Single Table Inheritance là gì ? STI về cơ bản là ý tưởng sử dụng một bảng duy nhất để phản ánh cho nhiều model được kế thừa từ một model cha. Nó là một thành phần của ActiveRecord::Base. Trong cơ sở dữ liệu, model con dược xác định bởi trường type. Trong Rails bạn chỉ việc thêm trường type ...
1. Single Table Inheritance là gì ?
STI về cơ bản là ý tưởng sử dụng một bảng duy nhất để phản ánh cho nhiều model được kế thừa từ một model cha. Nó là một thành phần của ActiveRecord::Base. Trong cơ sở dữ liệu, model con dược xác định bởi trường type. Trong Rails bạn chỉ việc thêm trường type vào bảng hệ thống sẽ hiểu bạn đang thiết lập STI. Ví dụ bạn có model Employee. Các employee có thể chia làm hai loại: Manager hoặc Developer. Chúng có chung các thuộc tính nhưng hành vi của chúng khác nhau. Việc tạo hai bảng Manager và Developer là không cần thiết. Thay vào đó bạn chỉ cần sử dụng một bảng Employee để lưu dữ liệu của cả Manager và Developer và chúng được phân biệt nhau bởi trường type.
2. Sử dung STI khi nào ?
STI sử dụng khi các model có các trường và các function giống nhau. Thay vì bạn viết một chức năng nhiều lần cho nhiều modle khác nhau hoặc linh hoạt trong việc thêm các chức năng riêng biết cho từng model khác nhau, STI cho phép bạn lưu trữ dữ liệu của các model đó trong một bảng duy nhất trong khi vẫn có thể viết các chức năng riêng cho từng model. STI cung cấp đầy đủ các model method trong Rails như create, new, update_attributes … cho cả class cha và các class con được lưu trên một bảng duy nhất.
Chú ý rằng không sử dụng STI chỉ vì các model có vẻ giống nhau. Hãy chắc chắn rằng có một mối quan hệ hướng đối tượng giữa chúng. Một ví dụ thực thế là chúng ta không thể sử dụng bảng Vehicles để lưu data cho các model Car, Bicycle, Tank. Trong trường hợp trên sử dụng STI là không phù hợp vì một chiếc ô tô có đặc điểm và chức năng khác với một chiếc xe tăng và một chiếc xe đạp. Sẽ phù hợp hơn khi sử dụng STI cho bảng Car để lưu dữ liệu cho các phân loại xe như suv, sedan, hybrid. Các class con này có chức năng và phong cách riêng nhưng chia sẻ những được giống nhau của một chiếc xe ô tô. Sử dụng STI là một cách hiệu quả để làm tối giản và tránh bị lặp trong sơ đồ cơ sở dữ liệu
3. Cài đặt
Trước hết bạn tạo model Tribe:
rails g model tribe name:string
Tạo model Animal:
rails g model animal name:string age:integer race:string
Mặc định Active Record sẽ lưu tên của class con trong trường type nhưng bạn có thể đổi tên trường này thành bất cứ tên nào. Ở đây tôi sử dụng trường race.
Sau đó bạn thêm trường tribe_id vào Animal bằng cách thêm vào file migration hoặc tạo một migration mới như sau:
class AddTribeIdToAnimal < ActiveRecord::Migration def change add_column :animals, :tribe_id, :integer end end
chạy rake db:migrate
Như vậy tao có 2 model Tribe và Animal như bên dưới.
app/models/tribe.rb
class Tribe < ActiveRecord::Base has_many :animals end
app/models/animal.rb
class Animal < ActiveRecord::Base belongs_to :tribe self.inheritance_column = :race # We will need a way to know which animals # will subclass the Animal model def self.races %w(Lion WildBoar Meerkat) end end
Tạo thêm các class Lion, Meerkat và WildBoar
app/models/lion.rb
class Lion < Animal end
app/models/meerkat.rb
class Meerkat < Animal end
app/models/wild_boar.rb
class WildBoar < Animal end
Ở trên chúng ta khai báo quan hệ has_many giữa Tribe và Animal đồng thời tạo ba model con của model Animal. self.inheritance_column = :race được dùng để khai báo trường STI lưu tên của các model con trong cơ sở dữ liệu (nếu bạn sử dụng tên trường là type thì không cần phải khai báo dòng này).
Chú ý: Nếu bạn muốn bỏ STI cho model hoặc sử dụng trường type với mục đích khác bạn có thể sử dụng self.inheritance_column = :fake_column.
Như vậy coi như bạn đã khai báo thành công STI cho model Animal.
Thêm scope trong model cha cho mỗi model con và delegates in Tribe model giúp việc gọi object của các class con dễ dàng hơn
app/models/animals
scope :lions, -> {where(race: “Lion”)} scope :meerkats, -> {where(race: “Meerkat”)} scope :wild_boars, -> {where(race: “WildBoar”)}
app/models/tribes
delegate :lions, :meerkats, :wild_boars, to: :animals
Tạo dữ liệu cho SLI:
Nhờ STI giờ chúng tao có thể sử dụng các model Lion, WildBoar, Meerkar để tạo ra các object tương ứng. Trường race sẽ tự động được lưu bởi Active Record.
Tạo một Tribe
tribe = Tribe.create name: "LionTribe"
Tạo một vài object Animal thêm vào Tribe
tribe.animals << Lion.new name: "Simba", age: 10 tribe.animals << WildBoar.new name: "Pumba", age: 30 tribe.animals << Meerkat.new name: "Timon", age: 30
Nếu bạn khai báo delegate như phía trên bạn có thể sử dụng các method sau:
tribe.wild_boars, tribe.lions, tribe.meerkats, tribe.animals Animal.lions, Animal.meerkats, Animal.wild_boars Animal.all, Lion.all, Meerkat.all, WildBoar.all
4. Tổng kết
Qua bài viết trên tôi đã hướng dẫn cho các bạn những điều cơ bản nhất và những lợi ích nó mang lại. Chúc các bạn ứng dụng thành công trong dự án.