Single Table Inheritance (STI) trong Rails
Bài đăng này đã không được cập nhật trong 7 năm
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ử dụng 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. Khởi tạo các models và các relations
Bây giờ, chúng ta sẽ tạo ra các models và các mối quan hệ (relations).
Tạo Tribe model
rails g model tribe name:string
Tạo Animal model
rails g model animal name:string age:integer race:string
Như phần ở trên đã đề cập, chúng ta chỉ cần thêm cột race sẽ được sử dụng bởi Active Record để lưu lại tên mô hình phụ (submodels). Theo mặc định, nếu không tạo cột mới thì Active Record sẽ sử dụng cột mặc định là type.
Sau đó, bạn có thể thêm cột tribe_id vào bảng animals
rails g migration AddTribeIdToAnimals tribe_id:integer
Chạy lệnh trên ta có file migrate như sau:
class AddTribeIdToAnimal < ActiveRecord::Migration
def change
add_column :animals, :tribe_id, :integer
end
end
Run the migrations: rake db:migrate
Như vậy ta 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ệ hasmany giữa Tribe
và Animal
đồng thời tạo 3 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
trong 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. Kết luận
Bên trên chúng ta đã cài đặt mô hình STI và một số chức năng cơ bản bạn có thể làm. Như bạn có thể thấy, nó là hữu ích khi nếu mô hình cơ sở dữ liệu của bạn có nhiều models có nhiều thuộc tính giống nhau cần thừa kế từ một model khác, STI giúp bạn không cần tạo ra nhiều models khác nhau có các thuộc tính tương tự nhau mà chỉ cần có thêm 1 trường để phân chia. Nguồn: https://devblast.com/b/single-table-inheritance-with-rails-4-part-1
All rights reserved