Railsのモデルのscope

scopeとは

スコープを設定することで、絞り込み条件、すなわちクエリー用のメソッドの連続した呼び出し部分をまとめて名前をつけ、カスタムのクエリー用メソッドとして使うことができます。

複雑なSQLを何度も書かなくてよくなります。繰り返し利用される絞り込み条件をスッキリ読みやすくでき、保守性に優れて、レビュアーにとっても読みやすくなります。

同じことがcontrollerでもできるのですが『Skinny Controller, Fat Model(コントローラーのコード量を少なくして、モデルを分厚くする)』という書き方が推奨されています。 このような背景から、ファットモデルという設計が目指すべき設計ということになります。


scopeを使った例

Railsガイドの例を使って説明していきます。

クラスの内部でscopeメソッドを使用し、スコープが呼び出されたときに実行して欲しいクエリをそこで渡します。

 class Article < ApplicationRecord
   scope :published, -> { where(published: true) }
 end


scopeはクラスメソッドの定義と完全に一致しています。どちらの形式を使用しても構いません。

 class Article < ApplicationRecord
   def self.published
     where(published: true)
   end
 end


scope内でチェインすることも可能です。

 class Article < ApplicationRecord
   scope :published,               -> { where(published: true) }
   scope :published_and_commented, -> { published.where("comments_count > 0") }
 end


scopeを使った呼び出し

 category = Category.first
 category.articles.published # => [このカテゴリに属する、公開済みの記事]



引数を受け取ったscope

article.rb

 scope :by_category, ->(category_id) { where(category_id: category_id) }

search_articles_form.rb

 relation = relation.by_category(category_id) if category_id.present?

このように書いておけば、選択したカテゴリーに該当する記事を検索できるようになる。


 scope :title_contain, ->(word) { where('title LIKE ?', "%#{word}%") }
 title_words.each do |word|
   relation = relation.title_contain(word)
 end

  private

  # タイトルの検索内容を配列に入れている。
 def title_words
   title.present? ? title.split(nil) : []
 end

入力内容に該当する記事を検索できるようになる

URL

railsguides.jp