Nếu bạn đã từng học lý thuyết về cơ sở dữ liệu quan hệ thì bạn sẽ biết là các bảng trong một CSDL sẽ có thể có các mối quan hệ với nhau, như quan hệ 1-1, 1-n, n-n…v.v, nếu bạn chưa biết về lý thuyết quan hệ này thì tự tìm hiểu thêm, trong bài này mình không giải thích mà chỉ hướng dẫn cách mô tả các mối quan hệ này trong Rails.
Trong các bài trước chúng ta đã tạo một model là product
dùng để lưu thông tin của từng sản phẩm, model cart
có chức năng lưu thông tin của một giỏ hàng (mặc dù không có trường nào), bây giờ chúng ta sẽ tạo một model dùng để kết nối thông tin giữa sản phẩm và giỏ hàng.
Model đó sẽ có tên line_item,
chúng ta tạo model như sau:
C:\Projects\Rails\depot>rails generate scaffold line_item product_id:integer cart_id:integer ...
Model này sẽ có 2 cột dùng để lưu id của cart và product là product_id
và cart_id
.
Tiếp theo chúng ta chạy lệnh rake db:migrate
để tạo bảng.
C:\Projects\Rails\depot>rake db:migrate == 20161121012142 CreateLineItems: migrating ========================================== -- create_table(:line_items) -> 0.0150s == 20161121012142 CreateLineItems: migrated (0.0158s) =================================
Vậy là chúng ta đã tạo xong bảng trong CSDL và lớp model, tuy nhiên hiện tại model lại không biết gì về các quan hệ này, chúng ta phải gọi thêm một số phương thức nữa, các phương thức mà chúng ta sẽ dùng được gọi là Associations.
Chúng ta sửa file cart.rb
trong thư mục app/models
như sau:
class Cart < ActiveRecord::Base has_many :line_items, :dependent => :destroy end
Phương thức has_many :line_items
chỉ định một đối tượng Cart
sẽ có nhiều liên kết tới đối tượng LineItem
, tham số :dependent
=> :destroy
cho biết khi một đối tượng Cart
bị hủy thì đối tượng LineItem
đó cũng sẽ bị hủy theo.
TIếp theo chúng ta sửa file model line_item.rb
như sau:
class LineItem < ActiveRecord::Base belongs_to :product belongs_to :cart end
Phương thức belongs_to
cho biết một đối tượng LineItem
sẽ “thuộc về” một đối tượng nào đó, ở đây là Product
và Cart
. Phương thức này bắt buộc Rails phải “hiểu” rằng sẽ không bao giờ tồn tại một đối tượng LineItem
nào mà không liên kết/thuộc về một đối tượng Cart
và Product
nào đó.
Khi một đối tượng LineItem
“thuộc về” một đối tượng Product
và Cart
thì chúng ta có thể truy xuất các đối tượng Product
và Cart
đó trong đối tượng LineItem
.
Cuối cùng chúng ta sửa file model product.rb
như sau:
class Product < ActiveRecord::Base validates :title, :description, :image_url, :presence => true validates :price, :numericality => {:greater_than_or_equal_to => 1.0} validates :title, :uniqueness => true validates :image_url, :format => { :with => %r{\.(gif|jpg|png)\Z}i, :message => 'Chi nhan file GIF, JPG, PNG' } has_many :line_items before_destroy :check_if_has_line_item private def check_if_has_line_item if line_items.empty? return true else errors.add(:base, 'This product has a LineItem') return false end end end
Tương tự với model Cart,
một đối tượng Product
cũng sẽ có nhiều liên kết tới một đối tượng LineItem.
before_destroy :check_if_has_line_item
Ngoài ra ở đây chúng ta còn sử dụng tới một hàm Callback của lớp ActiveRecord::Base
là before_destroy
, hàm Callback ở đây là các hàm sẽ được gọi trước hoặc sau khi Rails thực hiện một thao tác có cập nhật lên CSDL, chẳng hạn như before_destroy
tức là gọi một phương thức nào đó trước khi hủy một dòng trong bảng, ở đây là phương thức check_if_has_line_item
do chúng ta tự định nghĩa, phương thức này chúng ta để phạm vi truy cập là private
.
private def check_if_has_line_item if line_items.empty? return true else errors.add(:base, 'This product has a LineItem') return false end end
Phương thức này sẽ kiểm tra xem một đối tượng Product
có liên kết nào tới một đối tượng LineItem
nào không, nếu không thì trả về true,
ngược lại thì chúng ta tạo một phần tử trong thuộc tính errors
có khóa là :base
và giá trị là 'This product has a LineItem',
rồi trả về false
.
Nếu phương thức ở before_destroy
trả về false
thì Rails sẽ không hủy đối tượng đó.
Bạn có thể tìm hiểu thêm về các hàm callback ở đây:
http://guides.rubyonrails.org/active_record_callbacks.html
Bạn có thể tìm hiểu thêm về các phương thức Associations ở đây:
http://guides.rubyonrails.org/association_basics.html