LoginSignup
331
260

More than 3 years have passed since last update.

Railsのモデルのscopeを理解しよう

Last updated at Posted at 2018-11-30

はじめに

Railsを学習していて、「モデルのscopeってどうやって使うんだろう?」となったことがありました。Railsのモデルのscopeはどのような場面で、どうやって使うのかについて説明して行こうと思います。

modelのscopeとは

そもそもmodelのscopeとは、Active Recordの機能の一部です。以下Ruby on Rails ガイドより

スコープを設定することで、関連オブジェクトやモデルへのメソッド呼び出しとして参照される、よく使用されるクエリを指定することができます。スコープでは、where、joins、includesなど、これまでに登場したすべてのメソッドを使用できます。どのスコープメソッドも、常にActiveRecord::Relationオブジェクトを返します。このオブジェクトに対して、別のスコープを含む他のメソッド呼び出しを行なうこともできます。
単純なスコープを設定するには、クラスの内部でscopeメソッドを使用し、スコープが呼び出されたときに実行して欲しいクエリをそこで渡します。

上記を簡潔にするとモデルのscopeとは複数のクエリをまとめたメソッドになります。

modelのscopeを使うと使わないとでは何が違うか?

実際にUserを例に説明して行こうと思います。
Userコントローラーのindex内で@userswhere(deleted: false).order(created_at: :desc)のようにUserを呼び出しています。(deletedカラムfalseの時、created_atカラム降順で呼び出す)しかし、whereorderのクエリメソッドが連結しています。これでも呼びだせるため良いですが、メソッドが連結しているため若干読みづらいです。

app/controller/users_controller.rb
class UsersController < ApplicationController
  ...
  def index
    @users = User.where(deleted: false).order(created_at: :desc)
  end
  ...
end

Userモデルで以下のように「active」、「sorted」、「recent」という3つのscopeを定義しましょう。

app/model/user.rb
class User < ApplicationRecord
  ...
    # deletedカラムがfalseであるものを取得する
    scope :active, -> { where(deleted: false) }
    # created_atカラムを降順で取得する
    scope :sorted, -> { order(created_at: :desc) }
    # activeとsortedを合わせたもの
    scope :recent, -> { active.sorted }
  ...
end

scopeは再利用が可能です。そのため、繰り返し何度も呼び出す際は毎回クエリメソッドを連結で呼び出すよりも、scopeを定義したほうがコードがスッキリして読みやすくなるということです。それなら以下のようにしても良いのでは?と思われる方もいるかと思います。

scope :recent, -> { where(deleted: false).order(created_at: :desc) }

なぜ、3つのscopeに分けたかというと、再利用という点に着目した際にwhere(deleted: false).order(created_at: :desc)とあらかじめ連結しておくよりもwhere(deleted: false)order(created_at: :desc)に分けておくことにより、個々でつかうことが可能となるため広く利用可能になるからです。また、activesortedを合わせたrecentを定義することでコントローラーから簡単に呼び出すごとができます。

app/controller/users_controller.rb
class UsersController < ApplicationController
  ...
  def index
    @users = User.recent
  end
  ...
end

比較するとscopeを定義して、複数のクエリを集約したメソッドrecentを作ったほうが簡潔に書けると思います。

app/controller/users_controller.rb
class UsersController < ApplicationController
  ...
  def index
    # scope recentを定義しなかった場合
    @users = User.where(deleted: false).order(created_at: :desc)
    # scope recentを定義した場合
    @users = User.recent
  end
  ...
end

まとめ

  • モデルのscopeを定義することにより、複数のクエリを一つのメソッドとしてまとめることができる。
  • コントローラーで複数のクエリを書くよりもscopeを使えば、コードがスッキリする。
331
260
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
331
260