@changed_attributes trong việc log bằng PaperTrail
Bài đăng này đã không được cập nhật trong 9 năm
Trong việc log bằng PaperTrail, đôi khi việc đưa thêm các gía trị mong muốn vào phần lưu log gặp khó khăn, bài viết sau đây hi vọng gíup đỡ phần nào công việc của bạn.
Link gem PaperTrail https://github.com/airblade/paper_trail
Các model được sử dụng ở trong bài viết https://viblo.asia/pham.huy.cuong/posts/E7bGo9LOR5e2
Vậy ta có các bảng User, House, HousesUser
Thử update bản ghi đầu tiên của bảng User trường name và xem log của bản ghi này
irb(main):011:0> User.first.update name: "new_name"
irb(main):012:0> User.first.versions.last.object_changes
=> "---\nname:\n- name\n- new_name\nupdated_at:\n- 2016-04-08 09:17:04.741919000 Z\n- 2016-04-08 09:17:40.047259615 Z\n"
Chuyển thành hash ta gọi changeset
irb(main):015:0> User.first.versions.last.changeset
=> {"name"=>["name", "new_name"], "updated_at"=>[2016-04-08 09:17:04 UTC, 2016-04-08 09:17:40 UTC]}
Bây giờ gỉa sử ta muốn đưa thêm trường version_index lưu lại lượt thay đổi của record vào object_changes, ta thêm vào model User
before_save :create_version_index
private
def create_version_index
@changed_attributes ||= {}
@changed_attributes.merge! version_index: nil
end
def version_index
versions.count
end
Update lại trường name ta thấy
irb(main):016:0> User.first.update name: "name"
irb(main):017:0> User.first.versions.last.changeset
=> {"name"=>["new_name", "name"], "version_index"=>[nil, 10], "updated_at"=>[2016-04-08 09:17:40 UTC, 2016-04-08 09:24:11 UTC]}
Thử gán lại trường name nhưng chưa save ta sẽ hiểu rõ hơn về @changed_attributes
irb(main):018:0> user = User.first
irb(main):019:0> user.name = "new_name"
irb(main):020:0> user.changed_attributes
=> {"name"=>"name"}
irb(main):025:0> user.changes
=> {"name"=>["name", "new_name"]}
Trong PaperTrail, hàm sinh ra object_changes là changes_for_paper_trail
https://github.com/airblade/paper_trail/blob/master/lib/paper_trail/has_paper_trail.rb#L395
def changes_for_paper_trail
notable_changes = changes.delete_if { |k, _v| !notably_changed.include?(k) }
self.class.serialize_attribute_changes_for_paper_trail!(notable_changes)
notable_changes.to_hash
end
PaperTrail dùng hàm changes để lấy ra những attributes thay đổi và gía trị cũ và mới của nó, hàm changes gọi đến hàm changed_attributes để tìm những attributes có gía trị thay đổi.
def changed
changed_attributes.keys
end
def changed_attributes
@changed_attributes ||= ActiveSupport::HashWithIndifferentAccess.new
end
Ở đây ta thấy việc thay đổi @changed_attributes sẽ gíup ta đưa thêm các gía trị muốn thêm vào hash giữ các attributes và các gía trị thay đổi của nó.
Đối với các attribute thông thường thì việc đưa thêm value là như vậy, còn với các mối quan hệ has_many thì việc log sẽ như thế nào?, tại bài viết https://viblo.asia/pham.huy.cuong/posts/E7bGo9LOR5e2 đã trình bày cách log đối với dạng quan hệ này theo cách dùng cách tạo version cho bảng trung gian HousesUser, còn bằng cách sử dụng @changed_attributes thì có thể làm theo cách sau:
Tại model User ta thay đổi 1 chút trong cách khai báo has_many đối với houses
has_many :houses, through: :houses_users,
before_add: :create_association_params,
before_remove: :create_association_params
before_add, before_remove là hàm call_back để mỗi thi thêm hoặc bớt các mối quan hệ giữa 1 user với 1 house thì sẽ gọi đến hàm create_association_params, thêm vào private của model User:
def create_association_params record
@changed_attributes ||= {}
unless @changed_attributes[:house_ids]
@changed_attributes.merge! house_ids: old_house_ids
end
end
def old_house_ids
houses.pluck :id
end
Thử update house_ids cho bản ghi đầu tiên của User ta thấy:
irb(main):007:0> u = User.first
irb(main):008:0> u.update house_ids: [1,2]
irb(main):009:0> u.versions.last.changeset
=> {"house_ids"=>[[5, 6], [1, 2]], "updated_at"=>[2016-04-13 01:39:04 UTC, 2016-04-13 01:39:28 UTC], "version_index"=>[nil, 48]}
Chú ý house_ids đã được log lại "house_ids"=>[[5, 6], [1, 2]].
Cảm ơn bạn đã đọc và hi vọng bài viết giúp ích phần nào công việc của bạn.
All rights reserved