Help us understand the problem. What is going on with this article?

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

はじめに

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を使えば、コードがスッキリする。
ozin
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした