Đây là một bài mình dịch lại của một tác giả trên mạng, mình cũng làm Ruby nên khá là hứng thú với chủ đề này.
Đào sâu vào code của ActiveRecord::Relation
, tôi như vớ được một mỏ vàng nhưng lại ít được để mắt tới. Sau đây là Top 10 method ít được dùng nhất, nhưng lại khá hữu dụng mà tôi đã tổng hợp lại cho các bạn quan tâm.
10. first_or_create
first_or_create
thì khá là quen thuộc rồi:
Book.where(:title => 'Tale of Two Cities').first_or_create
và chỉ nghe tên là bạn đủ hiểu nó sẽ làm gì. Cụ thể, bạn sẽ muốn tìm một record với các thuộc tính cho trước, nếu không tìm thấy, tạo một record chứa các thuộc tính đó, và có thể thêm một vài thuộc tính khác. Để làm được điều này, rất đơn giản, bạn thêm block cho first_or_create
:
Book.where(:title => 'Tale of Two Cities').first_or_create do |book|
book.author = 'Charles Dickens'
book.published_year = 1859
end
9. first_or_initialize
Tương tự như trên, nhưng nếu bạn chưa muốn save vào DB ngay, có thể dùng first_or_initialize
Book.where(:title => 'Tale of Two Cities').first_or_initialize
8. scoped
Note: Khó dịch quá, với lại scoped
đã bị bỏ đi từ Rails v3.1.0, các bạn có thể xem thêm ở API Dock.
7. none
(Rails 4 only)
Có những lúc bạn muốn kêt quả trả về là một ActiveRecord::Relation
không chứa một object nào cả. Nếu trả về một mảng rỗng hoặc một "cái gì đó" rỗng thì không phải là ý hay khi khách hàng của bạn muốn một relation object
. Khi đó ta dùng none
:
def filter(filter_name)
case filter_name
when :all
scoped
when :published
where(:published => true)
when :unpublished
where(:published => false)
else
none
end
end
Note: Bạn rất nên cẩn thận khi dùng none
. Rails 4 đã hỗ trợ, còn Rails 3 thì không. Nhưng nếu bạn muốn code bằng tay thì cũng đơn giản, hãy xem trên stackoverflow xem sao.
6. find_each
Khi bạn cần chạy lặp qua cả ngàn records, chắc bạn sẽ không muốn dùng each
. Nghĩa là chỉ cần query 1 lần, cả ngàn records sẽ chui sẵn vào bộ nhớ. Nếu bạn có thừa thãi bộ nhớ, cứ việc làm như trên. Ngược lại, việc này sẽ có thể làm treo app của bạn bất cứ lúc nào. find_each
sẽ chỉ query lần lượt một số records nhất định trong một thời điểm (mặc định là 1000), cho nên bạn sẽ không tốn bộ nhớ khi bạn có quá nhiều dữ liệu.
Book.where(:published => true).find_each do |book|
puts "Do something with #{book.title} here!"
end
Chú ý là bạn không thể order
được khi dùng find_each
, nếu bạn cố dùng thì cũng bị bỏ qua mà thôi.
5. to_sql
và explain
ActiveRecord
thật sự rất ngon nghẻ, nhưng đôi khi nó trả ra những kết quả mà bạn không hề mong muốn. Chạy những lệnh trên trong khi debug hoặc trong rails console
, để xem câu query mà bạn dựng bằng Rails có chuẩn xác hay không.
Library.joins(:book).to_sql
# => SQL query for you database.
Libray.joins(:book).explain
# => Database explain for the query.
4. find_by
(Rails 4 only)
Bạn có một câu dài dòng kiểu như:
Book.where(:title => 'Three Day Road', :author => 'Joseph Boyden').first
Tại sao lại không dùng find_by
:
Book.find_by(:title => 'Three Day Road', :author => 'Joseph Boyden')
cũng cho kết quả y hệt!
Chú ý: Thật cẩn thận khi dùng find_by
, chỉ Rails 4 mới hỗ trợ, Rails 3 thì không.
3. scoping
Ta có thể nhóm kết quả của các methods trong class lại để sử dụng ngay kết quả trong một block:
Comment.where(:post_id => 1).scoping do
Comment.first # SELECT * FROM comments WHERE post_id = 1
end
2. pluck
Chẳng hay bạn có muốn một mảng các giá trị của một (hoặc nhiều) column trong một mớ records nào đó? Có một số cách khá tù:
published_book_titles = Book.published.select(:title).map(&:title)
Hay tù hơn:
published_book_titles = Book.published.map(&:title)
Thay vào đó, hãy dùng pluck
:
published_book_titles = Book.published.pluck(:title)
# => ["Norwegian Wood", "The Catcher in the Rye", ...]
1. merge
Tôi khó mà sống được mà thiếu cục vàng này, nhưng lạ thay nó lại không được đề cập trong tài liệu gốc, cũng không có bài hướng dẫn hay sách báo nào cả. Nó cho phép bạn joins với một model, sau đó thì dùng một scope của chính model được joined đó:
class Account < ActiveRecord::Base
# Returns all the accounts that have unread messages.
def self.with_unread_messages
joins(:messages).merge( Message.unread )
end
end
Bài gốc: The 10 Most Underused ActiveRecord::Relation Methods.