10 ActiveRecord::Relation Methods ít được sử dụng trong Rails

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

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

Khi bạn muốn ActiveRecord::Relation trả về toàn bộ records của class, có thể dùng scoped. Từ Rails v3.1.0 thì method này đã bị xóa bỏ.

def search(query)
  if query.blank?
    scoped
  else
    q = "%#{query}%"
    where("title like ? or author like ?", q, q)
  end
end

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.

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

5. to_sql and explain

ActiveRecord thật sự rất ngon, 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

Bạn có một câu dài dòng kiểu như:

Book.where(:title => 'Three Day Road', :author => 'Joseph Boyden').first

Thay vào đó, bạn có thể sử dụng find_by:

Book.find_by(:title => 'Three Day Road', :author => 'Joseph Boyden')

cũng cho kết quả y hệt!

Note: Thật cẩn thận khi dùng find_by, chỉ Rails 4 trở lên mới hỗ trợ, Rails 3 thì không.

3. scoping

Bạn có thể viết lại 1 class method riêng cho 1 trường hợp nào đó giống như scope với models. Tham khảo ví dụ dưới đây được lấy từ tài liệu của Rails:

Comment.where(:post_id => 1).scoping do
  Comment.first # SELECT * FROM comments WHERE post_id = 1
end

2. pluck

Khi muốn đưa ra array giá trị 1 cột của các records, thông thường ta hay sử dụng

published_book_titles = Book.published.select(:title).map(&:title)

hay là

published_book_titles = Book.published.map(&:title)

thì ta có thể dùng pluck để thay thế

published_book_titles = Book.published.pluck(:title)
# => ["Norwegian Wood", "The Catcher in the Rye", ...]

1. merge

Đây là 1 method rất hữu dụng khi sử dụng trong ActiveRecord::Relation. Bạn có thể vừa joins bảng vừa lọc với 1 scope nào đó trong models

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 viết được dịch từ The 10 Most Underused ActiveRecord::Relation Methods