Active Record Callbacks trong Rails
1. Vòng đời của một Object Trong suốt quá trình hoạt động của một ứng dụng Rails, Object có thể được tạo ra, cập nhật hoặc bị xóa bỏ. Active Record cung cấp cho bạn những phương thức (CallBacks) gắn vào vòng đời của mỗi Object giúp bạn có thể dễ dàng quản lý ứng dụng cũng như dữ liệu của ...
1. Vòng đời của một Object
Trong suốt quá trình hoạt động của một ứng dụng Rails, Object có thể được tạo ra, cập nhật hoặc bị xóa bỏ. Active Record cung cấp cho bạn những phương thức (CallBacks) gắn vào vòng đời của mỗi Object giúp bạn có thể dễ dàng quản lý ứng dụng cũng như dữ liệu của nó. CallBacks cho phép bạn kích hoạt một logic trước hoặc sau sự thay đổi trạng thái của một Object.
2. Tổng quan về CallBacks
CallBack là những phương thức được gọi những thời điểm nhất định trong vòng đời của một đối tượng. Với Callbacks có thể viết code mà sẽ chạy bất cứ khi nào một Active Record Object được tạo ra, lưu, cập nhật, xóa, xác nhận, hoặc tải từ cơ sở dữ liệu.
2.1 Khởi tạo CallBacks
Để sử dụng CallBacks trước hết phải khai báo nó trong Model của Object. Bạn có thể viết một phương thức thông thường rồi sử dụng macro-style để khai báo nó như một CallBacks. Ví dụ :
class User < ActiveRecord::Base validates :login, :email, presence: true before_validation :ensure_login_has_a_value protected def ensure_login_has_a_value if login.nil? self.login = email unless email.blank? end end end
hoặc cũng có thể viết dưới dạng block:
class User < ActiveRecord::Base validates :login, :email, presence: true before_create do self.name = login.capitalize if name.blank? end end
CallBacks cũng có thể được khai báo cho một sự kiện cụ thể trong vòng đời của 1 Object:
class User < ActiveRecord::Base before_validation :normalize_name, on: :create # :on takes an array as well after_validation :set_location, on: [ :create, :update ] protected def normalize_name self.name = self.name.downcase.titleize end def set_location self.location = LocationService.query(self) end end
Hãy khai báo CallBacks là những phương thức private hay protected. Vì nếu bạn khai báo là một phương thức public thì CallBacks có thể được gọi từ ngoài Model và vi phạm các nguyên tắc đóng gói của lập trình hướng đối tượng.
3. Danh sách CallBacks
Dưới đây là danh sách tất cả các Active Record CallBacks có sẵn được liệt kê theo các sự kiện của một Object:
3.1 Creating an Object
before_validation
after_validation
before_save
around_save
before_create
around_create
after_create
after_save
after_commit/after_rollback
3.2 Updating an Object
before_validation
after_validation
before_save
around_save
before_update
around_update
after_update
after_save
after_commit/after_rollback
3.3 Destroying an Object
before_destroy
around_destroy
after_destroy
after_commit/after_rollback
Lưu ý : Phương thức after_save được chạy sau cả 2 sự kiện là create và update. Nếu cụ thể hơn bạn muốn dùng riêng CallBack cho từng sự kiện khác nhau thì bạn có thể sự dụng after_create và after_up##date.
3.4 after_initialize và after_find
CallBack after_initialize gọi khi một Active Record được khởi tạo hoặc được gọi ra từ cơ sở dữ liệu. Phương thức này tỏ ra hết sức hữu dụng nhầm tránh việc ghi đè trực tiếp lên dữ liệu. CallBack after_find sẽ được gọi là bất cứ khi nào gọi một Active Record từ cơ sở dữ liệu. after_find được gọi trước after_initialize nếu cả hai cùng được sử dụng. Không có callback before_initialize và before_find.
class User < ActiveRecord::Base after_initialize do |user| puts "You have initialized an object!" end after_find do |user| puts "You have found an object!" end end > User.new You have initialized an object! => #<User id: nil> > User.first You have found an object! You have initialized an object! => #<User id: 1>
3.5 after_touch
CallBack after_touch gọi khi một Active Record tác động vào. Ví dụ:
class User < ActiveRecord::Base after_touch do |user| puts "You have touched an object" end end > u = User.create name: "Kuldeep" => #<User id: 1, name: "Kuldeep", created_at: "2015-12-25 12:17:49", updated_at: "2015-12-25 12:17:49"> > u.touch You have touched an object => true
Bạn cũng có thể sử dụng nó với quan hệ belongs_to
class Employee < ActiveRecord::Base belongs_to :company, touch: true after_touch do puts "An Employee was touched" end end class Company < ActiveRecord::Base has_many :employees after_touch :log_when_employees_or_company_touched private def log_when_employees_or_company_touched puts "Employee/Company was touched" end end > @employee = Employee.last => #<Employee id: 1, company_id: 1, created_at: "2015-12-25 17:04:22", updated_at: "2015-12-25 17:05:05"> # triggers @employee.company.touch > @employee.touch Employee/Company was touched An Employee was touched => true
4. Chạy CallBacks
Các phương thức khi chạy sẽ kích hoạt chạy callbacks:
create create! decrement! destroy destroy! destroy_all increment! save save! save(validate: false) toggle! update_attribute update update! valid?
Thêm vào đó callback after_find sẽ được chạy sau khi cách phương thức find chạy:
all first find find_by find_by_* find_by_*! find_by_sql last
5. Bỏ qua CallBacks
Giống như validations, CallBacks cũng có thể được bỏ qua bằng các phương thức :
decrement decrement_counter delete delete_all increment increment_counter toggle touch update_column update_columns update_all update_counters
Chú ý: Bạn nên thận trọng khi sử dụng các phương thức này. Việc bỏ qua callback có thể dẫn đễn dữ liệu không hợp lệ nếu bạn để những xử lý logic quan trọng trong callback
6. CallBack cùng với Relational
CallBackscó thể làm việc thông qua các mối quan hệ. Giả sử một user có nhiều bài viết. Bài viết của user sẽ được xóa nếu người dùng bị xóa. Bạn có thể sử dụng callback để thực hiên điện điều đó :
class User < ActiveRecord::Base has_many :articles, dependent: :destroy end class Article < ActiveRecord::Base after_destroy :log_destroy_action def log_destroy_action puts "Article destroyed" end end > user = User.first => #<User id: 1> > user.articles.create! => #<Article id: 1, user_id: 1> > user.destroy Article destroyed => #<User id: 1>
7. CallBacks có điều kiện
Giống như validation, bạn cũng có thế gọi callbacks với 1 điều kiện. Bạn có thể sử dụng :if hoặc :unless cùng với symbol, string, proc hoặc array.
7.1 Với symbol
Bạn có thể kết hợp :if và :unless với một symbol tương ứng với tên của một phương thức được gọi trước callback đó để quyết định việc callback đó có được thực hiện hay không. Đây là các thường xuyên được sử dụng nhất
class Order < ActiveRecord::Base before_save :normalize_card_number, if: :paid_with_card? end
7.2 Với String
class Order < ActiveRecord::Base before_save :normalize_card_number, if: "paid_with_card?" end
7.3 Với Proc
Cách này được sử dụng khi điều kiện của bạn sử dụng ngắn, thường là 1 dòng
class Order < ActiveRecord::Base before_save :normalize_card_number, if: Proc.new {|order| order.paid_with_card? } end
7.4 Kiết hợp nhiều điều kiện
Rails cũng cho phép bạn kết hợp nhiều điều kiện lại với nhau. Ví dụ
class Comment < ActiveRecord::Base after_create :send_email_to_author, if: :author_wants_emails?, unless: Proc.new {|comment| comment.article.ignore_comments?} end