Monday, February 2, 2009

search with named scopes

複数条件の AND 検索機能は Web アプリケーションを実装すると必ずといっていいほど実装するかと思います。
例えば、フォームからの入力で
params  # => {:city => "tokyo", :age => 30}
のようなパラメータが与えられるとすると、 fat controller を気にしなければ rails のアクションの実装は以下のようにやってました。
def index
conds, args = [], {}
unless (city = params[:city]).blank?
conds << "city = :city"
args[:city] = city
end
unless (age = params[:age]).blank?
conds << "age = :age"
args[:age] = age
end

if conds.blank?
@users = User.find(:all)
else
@users = User.find(:all, :conditions => [conds.map{|c|"(#{c})"}.join(" AND "), args])
end
end


named scope を使うと
class User
named_scope :city, lambda{|city| {:conditions => ["city = ?", city]}}
named_scope :age, lambda{|age| {:conditions => ["age = ?", age]}}
end

def index
@users = [:city, :age].inject(User) do |ret, scope|
params[scope].blank? ? ret : ret.send(scope, params[scope])
end.all
end

すばらしいじゃないですか。

検索してみるとすでに同じ事を思いついている人はたくさんいるようですね。

(上記、実際に動かしたコードをブログ用に書き換えたのでどっか間違っていたらごめんなさい。)

1 comment:

hiroshi saito said...

inject が User そのままを返したりする場合に対応するために .all を追加しました。(Thanks shingo)