Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
225
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

@ozin

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を使えば、コードがスッキリする。
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
225
Help us understand the problem. What are the problem?