Dùng PaperTrail log cho quan hệ has_and_belongs_to_many
Trong việc sử dụng Gem PaperTrail (https://github.com/airblade/paper_trail) để log cho các mối quan hê has_and_belongs_to_many đôi khi gặp nhiều vấn đề, bài viết sau hi vọng giúp ích phần nào trong công việc của bạn. https://github.com/airblade/paper_trail#associations phần này để tạo association ...
Trong việc sử dụng Gem PaperTrail (https://github.com/airblade/paper_trail) để log cho các mối quan hê has_and_belongs_to_many đôi khi gặp nhiều vấn đề, bài viết sau hi vọng giúp ích phần nào trong công việc của bạn. https://github.com/airblade/paper_trail#associations phần này để tạo association cho phần log.
Gỉa sử có 2 model User và House
class User < ActiveRecord::Base has_paper_trail has_and_belongs_to_many :houses end class House < ActiveRecord::Base has_paper_trail has_and_belongs_to_many :users end create_table "houses", force: :cascade do |t| t.string "name" t.datetime "created_at", null: false t.datetime "updated_at", null: false end create_table "users", force: :cascade do |t| t.string "name" t.datetime "created_at", null: false t.datetime "updated_at", null: false end
Paper trail không tạo version đối với quan hệ has_and_belongs_to_many, do vậy phải chuyển thành dạng quan hệ has_many và thêm has_paper_trail vào model quan hệ (house_user) trước khi muốn tạo log
class User < ActiveRecord::Base has_paper_trail has_many :houses_users has_many :houses, through: :houses_users end class House < ActiveRecord::Base has_paper_trail has_many :houses_users has_many :users, through: :houses_users end class HousesUser < ActiveRecord::Base has_paper_trail belongs_to :user belongs_to :house end
Bây giờ, tại console ta thử gán cho phần tử đầu tiên của User quan hệ với 1 phần tử của House
irb(main):011:0> User.first.house_ids = [3]
Ta sẽ thấy version của model HousesUser được tạo ra
SQL (0.1ms) INSERT INTO "versions" ("event", "created_at", "item_id", "item_type") VALUES (?, ?, ?, ?) [["event", "create"], ["created_at", "2016-01-08 10:34:03.595991"], ["item_id", 10], ["item_type", "HousesUser"]]
Và version_association được cũng đồng thời được tạo ra
SQL (0.1ms) INSERT INTO "version_associations" ("version_id", "foreign_key_name", "foreign_key_id") VALUES (?, ?, ?) [["version_id", 25], ["foreign_key_name", "user_id"], ["foreign_key_id", 1]] SQL (0.0ms) INSERT INTO "version_associations" ("version_id", "foreign_key_name", "foreign_key_id") VALUES (?, ?, ?) [["version_id", 25], ["foreign_key_name", "house_id"], ["foreign_key_id", 3]] (137.7ms) commit transaction => [3]
Version của model HouseUser được tạo ra với event: "create" nếu như quan hệ mới được thêm vào và event: "destroy" nếu như quan hệ mới được bỏ đi, còn tại model PaperTrail::VersionAssociation, bản ghi mới được tạo ra, ở bản ghi này ta thấy:
<PaperTrail::VersionAssociation id: 13, version_id: 25, foreign_key_name: "user_id", foreign_key_id: 1> <PaperTrail::VersionAssociation id: 14, version_id: 25, foreign_key_name: "house_id", foreign_key_id: 3>
version_id: là id của version được tạo cho model HouseUser, foreign_key_name: được lấy từ foreign_key taị model HouseUser, vì ở đây có 2 foreign_key chính là refrence tới 2 model House và User nên 2 bản ghi của PaperTrail::VersionAssociation được tạo ra để ghi lại những thay đổi này, foreign_key_id: là gía trị của attibute reference trên tại bản ghi của model HouseUser.
Từ đó ta có 2 cách viết scope để lấy ra các version của HouseUser tạo ra mỗi khi thêm các quan hệ giữa 1 User với 1 House:
PaperTrail::Version.where(item_type: "HousesUser").where_object user_id: 1
Cách này sử dụng 1 trong 2 scope do gem cung cấp sẵn là where_object, where_object_changes, tuy nhiên 2 scope này sử dụng cách so sánh string trong dữ liệu nên nhiều lúc đưa ra kết qủa chưa chính xác, tỉ lệ không nhiều lắm.
"SELECT `versions`.* FROM `versions` WHERE `versions`.`item_type` = 'HousesUser' AND (`versions`.`object` LIKE '%
user_id: 1
%')"
Đoạn ("versions"."object" LIKE "% user_id: 1 %") Là nguyên nhân có thể dẫn đến việc query ra những kết qủa sai, vì trong trường hợp nào đó, user có 1 trường là string và có gía trị như trong phần LIKE thì lúc query bản ghi cũng được lấy vào.
Cách dùng bảng PaperTrail::VersionAssociation để tìm được viết như sau:
PaperTrail::Version.where(item_type: "HousesUser").joins(:version_associations).where(version_associations: {foreign_key_name: "user_id", foreign_key_id: 1})
Từ câu query trên, ta có thể tìm được những thay đổi trong association và log lại được, cảm ơn bạn và hi vọng bài viết sẽ giúp ích phần nào trong công việc của bạn.