🐟

[Rails]モデルのscopeメソッド

2021/10/25に公開約3,400字

モデルのscopeメソッド

モデルにおけるscopeとはActiveRecordの機能の一つです。

モデル側で共通の条件式(クエリ処理)に名前を付けて定義し、その名前でメソッドのように呼び出すことができる仕組みのことです。

例)モデルにscope定義

(publishedカラムはtruefalse公開非公開を管理)

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

呼び出し

Blog.published

scopeを使うメリット

  • 条件式に名前を付けられるので直感的なコードになる
    ※ scopeを使わない場合

    Blog.where(published: true)
    

    ※ scopeを使った時(条件式はモデルにpublishedという名前で定義済み)

    Blog.published
    
  • 共通化により修正箇所が少なくて済む
    モデルにscope定義

    # blogsテーブルのpublishedがtureの記事を取得
    class Blog < ApplicationRecord
      scope :published, -> { where(published: true) } 
    end
    

    ↓ 上記のコードにlimitを追加したい時

    # scope定義のこの部分だけ変更するだけで済む。
    class Blog < ApplicationRecord
      scope :published, -> { where(published: true).limit(3) } 
    end
    
    # 呼び出しはBlog.publishedで変わらない
    
  • 記述コードが短くなる

    条件式はscopeを使わなくても、クラスメソッドとして定義できるが、コードが長くなってしまう。

    # 条件式はscopeを使って定義している
    class Blog < ApplicationRecord
      scope :published, -> { where(published: true) } 
    end
    
    # scopeを使わずにクラスメソッドとしても定義できるが、、
    class Blog < ApplicationRecord
      def self.published
        where(published: true).limit(2)
      end
    end
    

scopeメソッドの基本

下記のように定義します。

class モデル名 < ApplicationRecord
  scope :スコープの名前, -> { 条件式 }
end
  • 第一引数にシンボルを使ってスコープの名前を指定します。

  • 第二引数の-> { 条件式 }はlambdaと言い、処理の中でメソッドを定義してくれます。

  • ->(アロー関数)はlambdaのリテラルです。下記のような動きをします。
    引数を定義すると、呼び出すときにブロックに渡すことができます。

    # 定義
    foo = -> (x) { x + x }
    # 呼び出し
    foo[5]   # => 10
    foo.(5)  # => 10
    foo.call 5 # => 10
    
    # 通常のlambdaの書き方   
    bar = lambda { |x| x + x }
    bar.call 5 # => 10
    
class Blog < ApplicationRecord
  scope :published, -> { where(published: true).limit(2) } 
end
  • blogsテーブルから「公開」の記事を取得。さらに上限を2までにする。

引数を渡す

scopeメソッドは下記のように->の後ろに引数を定義すると、ブロックに引数を渡すことができます。

class モデル名 < ApplicationRecord
  scope :スコープの名前, -> (引数){ 条件式 }
end

引数を利用すると、scopeのpublishedを呼び出すときに、引数を渡せるので、取得上限件数をその都度変更できます。

# blogsテーブルから「公開」の記事を取得。さらに上限を2までにする。

class Blog < ApplicationRecord
  scope :published, -> (count){ where(published: true).limit(count) } 
end
# 呼び出し 上限が3の「公開」記事を取得できる。
Blog.published(3)

この様に引数を使うことで、汎用性のある条件式をつくることができます。

scopeを使ってメソッドチェーンを簡潔に

例としてBlogコントローラのindexで@boardswhere(published: true).order(created_at: :desc)のようにBlogを呼び出します。

class BlogController < ApplicationController

  def index
    @blogs = Blog.where(published: true).order(created_at: :desc)
  end

end

publishedtrueのblogのcreated_atカラムを降順に並び替えて取得しています。

これで呼び出すことはできるが、whereorderメソッドが連結しているので若干読みづらくなっています。

これをscopeメソッドを用いて「published」、「sorted」、「recent」という3つのscopeを定義してみます。

class Blog < ApplicationRecord
  # publishedカラムがtrueのものを取得
  scope :published, -> { where(published: true) } 

  # created_atカラムを降順で並び替え
  scope :sorted, -> {order(created_at: :desc)}

  # publishedがtrueのものを降順で並び替え
  scope :recent, -> {published.sorted}

end

scopeで定義すると、再利用が可能なので毎回同じクエリメソッドを使う場合はscope定義したほうがコードもスッキリして読みやすくなります。

class BlogController < ApplicationController

  def index
    # @blogs = Blog.where(published:true).order(created_at: :desc)
    @blogs = Blog.recent
  end

end

Discussion

ログインするとコメントできます