ActiveRecodeをもう少しSQLライクに使えるようにする
- 2013/09/12 (Thu) |
- Ruby on Rails |
- CM(0) |
- Edit |
- ▲Top
Rails4を使っていると、scopeメソッドを利用してActiveRecord(以下ARと略)を拡張することができると分かり、それならARで共通に使えるモジュールを作ったらいいじゃん、ということに。
これを使うには
とAR側でincludeし、
という具合に使うことができる。
ちなみにRails4からはActiveRecord::Relationというクラスが導入されていて、Rails3ではActiveRecordのwhereメソッド(正確にはActiveRecord::Base#where)の戻り値はARの配列だったが、4ではRelationが返ってくる。このクラスは言ってみればSQL生成器とでもいうべき機能を持っていて、#to_sql を呼び出すとその時点で蓄えている情報からSQL文を生成する。上記の例でいえばこんな感じだ。
しかもチェーンしていけるので便利。
RelationからARの配列(複数レコード)が欲しければto_aメソッド、1レコードだけ欲しければtakeメソッドを使えば良い。
Rails4ではARに用意されたscopeメソッドを使うと頻繁使うwhereメソッドの記述を簡便化できるので、これは大いに使った方が良いと思える。特にwhereメソッドでハッシュ記法を使うとSQL文は等価比較しかできないのでlikeや < といった演算子はそのままでは使えない。そういった部分を補完する意味でも役に立つ。
なお、whereメソッドを使ってSQLの「in」を指定したい場合は、配列を使い、範囲(between)を使いたい場合はRangeクラスが利用できる。
module Scope
def self.included(klass)
klass.extend ActiveRecord::ConnectionAdapters::Quoting
klass.class_eval{
scope :eq, ->(h){ where("#{h.keys.first.to_s} = #{quote(h.values.first)}") }
scope :not_eq, ->(h){ where("#{h.keys.first.to_s} != #{quote(h.values.first)}") }
scope :lt, ->(h){ where("#{h.keys.first.to_s} < #{quote(h.values.first)}") }
scope :less_than, ->(h){ where("#{h.keys.first.to_s} < #{quote(h.values.first)}") }
scope :lt_eql, ->(h){ where("#{h.keys.first.to_s} <= #{quote(h.values.first)}") }
scope :less_than_eql, ->(h){ where("#{h.keys.first.to_s} <= #{quote(h.values.first)}") }
scope :gt, ->(h){ where("#{h.keys.first.to_s} > #{quote(h.values.first)}") }
scope :greater_than, ->(h){ where("#{h.keys.first.to_s} > #{quote(h.values.first)}") }
scope :gt_eql, ->(h){ where("#{h.keys.first.to_s} >= #{quote(h.values.first)}") }
scope :greater_than_eql, ->(h){ where("#{h.keys.first.to_s} >= #{quote(h.values.first)}") }
scope :like, ->(h){ where("#{h.keys.first.to_s} like #{quote(h.values.first)}") }
}
end
end
これを使うには
class Book < ActiveRecord::Base
include Scope
end
とAR側でincludeし、
# 「あした」で中間一致する書名を持つ本のレコードを取得
books = Book.like(name: '%あした%').to_a
# 「あした」で中間一致する書名を持ち、4日前以降に出版された本のレコードを取得
books = Book.like(name: '%あした%').gt(publication_date: (Date.today - 5)).to_a
という具合に使うことができる。
ちなみにRails4からはActiveRecord::Relationというクラスが導入されていて、Rails3ではActiveRecordのwhereメソッド(正確にはActiveRecord::Base#where)の戻り値はARの配列だったが、4ではRelationが返ってくる。このクラスは言ってみればSQL生成器とでもいうべき機能を持っていて、#to_sql を呼び出すとその時点で蓄えている情報からSQL文を生成する。上記の例でいえばこんな感じだ。
irb > relation = Book.like(name: '%あした%')
irb > relation.to_sql
=> "SELECT \"books\".* FROM \"books\" WHERE (name like '%あした%')"
しかもチェーンしていけるので便利。
Book.where(name: 'あいうえお').where(auther: 'ほげ')
RelationからARの配列(複数レコード)が欲しければto_aメソッド、1レコードだけ欲しければtakeメソッドを使えば良い。
Rails4ではARに用意されたscopeメソッドを使うと頻繁使うwhereメソッドの記述を簡便化できるので、これは大いに使った方が良いと思える。特にwhereメソッドでハッシュ記法を使うとSQL文は等価比較しかできないのでlikeや < といった演算子はそのままでは使えない。そういった部分を補完する意味でも役に立つ。
なお、whereメソッドを使ってSQLの「in」を指定したい場合は、配列を使い、範囲(between)を使いたい場合はRangeクラスが利用できる。
# select * from books where author_name in (select name from authors where name = 'ほげ' の場合
Book.where(author_name: Author.where(name: 'ほげ').pluck(:name))
# 1年前から今日までの間を指定
# select * from books where publication_date between '2012-09-12' AND '2013-09-12'
# というSQL文を生成する
Book.where(publication_date: ((Date.today - 365)..Date.today))
PR
カレンダー
フリーエリア
最新CM
最新記事
(06/05)
(06/04)
(06/04)
(11/18)
(11/18)
ブログ内検索
最古記事
(09/15)
(09/20)
(09/27)
(09/27)
(10/11)
COMMENT