忍者ブログ

ぢみへんプログラミング日誌

[PR]

×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

ActiveRecodeをもう少しSQLライクに使えるようにする

Rails4を使っていると、scopeメソッドを利用してActiveRecord(以下ARと略)を拡張することができると分かり、それならARで共通に使えるモジュールを作ったらいいじゃん、ということに。
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

COMMENT

NAME
TITLE
MAIL (非公開)
URL
EMOJI
Vodafone絵文字 i-mode絵文字 Ezweb絵文字
COMMENT
PASS (コメント編集に必須です)
SECRET
管理人のみ閲覧できます
 
  

カレンダー

10 2024/11 12
S M T W T F S
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30

フリーエリア

最新CM

バーコード

ブログ内検索

Copyright ©  -- ぢみへんプログラミング日誌 --  All Rights Reserved

Design by CriCri / Material by petit sozai emi / powered by NINJA TOOLS / 忍者ブログ / [PR]