検索処理をスコープに入れる
元記事はこちら
翻訳する
BadSmell
class PostsController < ApplicationController
def index
@published_posts = Post.find(:all, :conditions => { :state => 'published' },
:limit => 10,
:order => 'created_at desc')
@draft_posts = Post.find(:all, :conditions => { :state => 'draft' },
:limit => 10,
:order => 'created_at desc')
end
end
コントローラー内で公開済み(published_posts)と下書き(draft_posts)の検索をするために、込み入った検索処理が書かれているが、以下の2点がよくない。
- コントローラー内に込み入った検索処理を書いてしまうと、たいていの場合長くなってしまい、読みづらい。
- 同じような込み入った検索処理は、他のコントローラーにも存在するだろうし、ロジックを変更させたくなった場合に、複数の箇所に手を入れないといけないという作りは、バグの温床になる。
Refactor((~3.0.9。Rails3.1~ はnamed_scope -> scope))
class PostsController < ApplicationController def index @published_posts = Post.published @draft_posts = Post.draft end end class Post < ActiveRecord::Base named_scope :published, :conditions => { :state => 'published' }, :limit => 10, :order => 'created_at desc' named_scope :draft, :conditions => { :state => 'draft' }, :limit => 10, :order => 'created_at desc' end
込み入った検索処理をコントローラーからモデルに移動した。ご覧の通り、コントローラーのコードが非常にシンプルになった。 込み入った検索処理はモデルにだけ存在するので、ロジックが変わったら、モデル内のコードを変更するだけでいい。
updated: Rails3以降では、named_scopeの代わりにscopeを使いましょう。
感想
scopeだとこんな感じかな
class Post < ActiveRecord::Base scope :published, :conditions => { :state => 'published' }, :limit => 10, :order => 'created_at desc' scope :draft, :conditions => { :state => 'draft' }, :limit => 10, :order => 'created_at desc' end
limit/orderの意味上の役割にもよるけど、ここを別scopeにして、mergeしてもいいのかな。
class PostsController < ApplicationController def index @published_posts = Post.published.view_limit @draft_posts = Post.draft.view_limit end end class Post < ActiveRecord::Base scope :published, -> { where(:state, "published" } scope :draft, -> { where(:state, "draft" } scope :view_limit, -> :conditions => :limit => 10, :order => 'created_at desc' end