Active Record Callbacks trong Rails
Bài đăng này đã không được cập nhật trong 8 năm
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
CallBacks
có 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
All rights reserved