12/08/2018, 15:14

Ruby on Rails ActiveRecord Query Interface

Mở đầu Rails cung cấp Active Record library - một ORM (Object Relational Mapping - như một layer nằm ở giữa ngôn ngữ lập trình và database, được viết bằng một ngôn ngữ lập trình hướng đối tượng giúp bạn có thể sử dụng chính ngôn ngữ lập trình đó để thao tác với database mà không cần viết các câu ...

Mở đầu

Rails cung cấp Active Record library - một ORM (Object Relational Mapping - như một layer nằm ở giữa ngôn ngữ lập trình và database, được viết bằng một ngôn ngữ lập trình hướng đối tượng giúp bạn có thể sử dụng chính ngôn ngữ lập trình đó để thao tác với database mà không cần viết các câu lệnh SQL). Các object class sẽ tương ứng với các bảng dữ liệu trong database, và các object instance sẽ tương ứng với các record trong các bảng dữ liệu đó. ActiveRecord cung cấp các finder method. Bằng các truyền các tham số để thực hiện query mà không cần sử dụng raw SQL. Dưới đây là một vài method hay dùng khi làm việc với ActiveRecord trong Rails

Retrieving object từ database

Method find

Truy xuất một hoặc nhiều record tương ứng với primary key, sẽ gây ra ngoại lệ exception ActiveRecord::RecordNotFound nếu không tìm thấy record Ví dụ sau tìm một record client với id là 1:

client = Client.find(1)
# => #<Client id: 1, user_name: "johnsmith">

Nếu muốn lấy nhiều record ta có thể pass một mảng primary key ví dụ như sau:

client = Client.find([1,10])
# => [#<Client id: 1,  user_name: "johnsmith">, #<Client id: 10,  user_name: "jane"]

Method take

Lấy ra một record (hoặc n records nếu truyền thêm tham số n) ngẫu nhiên không kèm theo order. Return nil nếu không có record nào được tìm thấy và không gây ra exception Ví dụ sau lấy ngẫu nhiên một 1 record client

client = Client.take
# => #<Client id: 2, user_name: "kate">

Tương tự lấy nhiều record ngẫu nhiên ta truyền tham số kiểu số (numberical) qua take:

client = Client.take(2)
# => [
#   #<Client id: 1, user_name: "ryo">,
#   #<Client id: 220, user_name: "songel">
# ]

Đặc biệt với method take! sẽ gây ra ActiveRecord:RecordNotFound exception nếu không tìm thấy record

Method first

Lấy ra một record đầu tiên được order theo primary key (default) hoặc truyền một tham số kiểu numberical n để lấy n records đầu tiên. Return nil nếu không tìm thấy record và không exception được gây ra. Đặc biệt với method first! sẽ gây ra ActiveRecord:RecordNotFound exception nếu không tìm thấy record Ví dụ lấy record đầu tiên và 3 record đầu tiên từ database:

client = Client.first          # => #<Client id: 1, user_name: "johnsmith">
client = Client.first(3)
# => [
#   #<Client id: 1, user_name: "johnsmith">,
#   #<Client id: 2, user_name: "kate">,
#   #<Client id: 3, user_name: "leo">
# ]

Method find_by

Tìm record đầu tiên phù hợp với các conditions, return nil nếu không tìm thấy record ngược lại với method find_by! gây ra exception ActiveRecord:RecordNotFound Ví dụ sau đây lấy record có user_name là "kate":

client = Client.find_by user_name: "kate"
# => #<Client id: 2, user_name: "kate">

Method 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

client_username = Client.actived.map(&:user_name)

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

client_username = Client.actived.pluck(:user_name)

Method first_or_initialize

Muốn lấy ra record nào đó, nếu nó không tồn tại thì tạo mới nó (ở đây chỉ init object chưa save object vào database). Sử dụng first_or_initialize Method source:

def first_or_initialize(attributes = nil, options = {}, &block)
  first || new(attributes, options, &block)
end

Ví dụ sau lấy ra object ứng với record đầu tiên với user_name là "kate", nếu không có thì sẽ khởi tạo object

Client.where(user_name: "kate").first_or_initialize

Method first_or_create

Tương tự như first_or_initialize nhưng khi muốn tạo mới 1 record và save luôn vào database thì có thể dùng first_or_create. Method source:

def first_or_create(attributes = nil, &block)
  first || create(attributes, &block)
end

Method first_or_create excute method create khác với method first_or_initialize excute method new. Dưới đây lấy ra object ứng với record đầu tiên với user_name là "kate" hoặc nếu không có thì sẽ tạo và save object vào database

Client.where(user_name: "kate").first_or_create

Scopes

  • Scope là một phần được support bởi Active Record.
  • Scope thường định nghĩa các query dùng chung và có thể gọi từ association objects hoặc model
  • Bản chất scope là ActiveRecord cung cấp một class method có tên là scope nhận 2 parameter. Parameter thứ nhất là tên của scope bạn muốn tạo. Parameter thứ hai là một lambda function, đây chính là phần thân của scope (phần thực thi)
  • Và scope luôn trả về ActiveRecord::Relation object Ví dụ dưới đây khai báo một scope published lấy các articles đã được published.
class Article < ApplicationRecord
  scope :published, -> { where(published: true) }
end

# call published scope
Article.published

Truyền tham số qua scope

class Article < ApplicationRecord
  scope :created_before, ->(time) {where("created_at < ?", time)}
end
Article.created_before(Time.zone.now)

Sử dụng conditions trong scope, nếu condition if là false thì scope luôn trả về ActiveRecord::Relation trong khi đó method class trả về nil.

class Article < ApplicationRecord
  scope :created_before, 
	->(time) {where("created_at < ?", time) if time.present?}
end

Default Scope

  • Nếu muốn áp dụng scope cho tất cả query cho model, ta sử dụng default_scope
  • default_scope cũng được áp dụng khi create / build một record, không áp dụng với update record.

Ví dụ áp dụng scope khi new record:

class Client < ApplicationRecord
  default_scope { where(active: true) }
end

Client.new          # => #<Client id: nil, active: true>

Merging of scopes

Giống như việc ta hợp 2 scope mệnh đề where sử dụng AND Ta có ví dụ sau thực hiện merge 2 scope với nhau:

class User < ApplicationRecord
  scope :active, -> { where state: 'active' }
  scope :inactive, -> { where state: 'inactive' }
end

User.active.inactive
# SELECT "users".* FROM "users" WHERE "users"."state" = 'active' AND "users"."state" = 'inactive'

Unscoped

Muốn xóa tất cả scope ta sử dụng method unscoped, nó sẽ xóa tất cả scope hoặc query trước unscoped trong query chains và query bình thường

Client.unscoped.all
# SELECT "clients".* FROM "clients"
 
Client.where(published: false).unscoped.all
# SELECT "clients".* FROM "clients"

Kết luận

Trên đây mình đã giới thiệu cơ bản về query interface trong ActiveRecord của Rails. Các bạn có thể tìm hiểu chi tiết tại:

  1. http://guides.rubyonrails.org/active_record_querying.html
  2. http://www.mitchcrowe.com/10-most-underused-activerecord-relation-methods/
0