読者です 読者をやめる 読者になる 読者になる

Rails Webook

自社のECを開発している会社で働いています。Rails情報やサービスを成長させる方法を書いていきます

Rails ModelのScope(スコープ)の使い方(scopeメソッドとdefault_scopeメソッド)

Rails入門 Rails Model

モデルのスコープ機能とは、共通的に使うクエリをモデルのメソッドのように定義できる機能です。
こうすることにより、複雑なSQLを何度も書かなくてよくなり、可読性と保守性を向上させることができます。

動作確認

  • Rails 4.1

目次

  1. スコープ(Scope)の概要
  2. スコープに引数を指定
  3. スコープのマージ
  4. デフォルトスコープの設定


1. スコープ(Scope)の概要

以下の2つの方法で「共通的に使うクエリをスコープ」として定義できます。

# 定義方法1: scopeメソッドで定義
class Post < ActiveRecord::Base
  scope :published, -> { where(published: true) }
end

# 定義方法2: クラスメソッドのように定義
class Post < ActiveRecord::Base
  def self.published
    where(published: true)
  end
end

次のように呼び出すことができます。

Post.published # => publishedカラムが"true"のPost達を取得

category = Category.first
category.posts.published # => カテゴリーに属するpublishedカラムが"true"のPost達を取得


2. スコープに引数を指定

scopeメソッドに引数を渡すこともできます。

# app/modes/post.rb
class Post < ActiveRecord::Base
  scope :created_before, ->(time) { where("created_at < ?", time) }
end

# 使用方法
Post.created_before(Time.local(2011)) #=> 2011年より前に作成されたPostレコードを取得


3. スコープのマージ

スコープ同士をマージすることにより、where句のAND条件を実現することができます。

# app/models/user.rb
class User < ActiveRecord::Base
  scope :inactive, -> { where state: 'inactive' }
  scope :finished, -> { where state: 'finished' }
end

# 使用方法
User.inactive.finished
# => SELECT "users".* FROM "users" WHERE "users"."state" = 'inactive' AND "users"."state" = 'finished'


また、joinsメソッドと一緒につかう為には、mergeメソッドをつかいます。

# 定義
class Category < ActiveRecord::Base
  has_many :posts
  # スコープでjoinsを行い、Postクラスのrecentスコープを利用する方法
  scope :with_posts, -> { joins(:posts).merge(Post.recent) }
end

class Post < ActiveRecord::Base
  belongs_to :category
  scope :recent, -> { where(created_at: Time.zone.now..3.days.ago) }
end


# 使い方 
Category.with_posts
#=> SELECT "categories".* FROM "categories" INNER JOIN "posts" ON "posts"."category_id" = "categories"."id" WHERE ("posts"."created_at" BETWEEN '2015-04-20 16:55:08.237023' AND '2015-04-17 16:55:08.237228')

デフォルトスコープの設定

モデルに対する全ての検索クエリでスコープを使いたい場合は、default_scopeメソッドが使えます。
この例では、論理削除したデータを意識したくない場合に使っています。

# app/models/customer.rb
class Customer < ActiveRecord::Base
  # 退会した顧客では、          removed_atカラムは "削除した日付"
  # 退会していない顧客では、removed_atカラムは "NULL"
  default_scope { where("removed_at IS NULL") } 
end

# 使用方法
# 全てのクエリにデフォルトスコープで指定した条件がつけられる
Customer.all
# => SELECT "customers".* FROM "customers" WHERE (removed_at IS NULL)


よくある?Rails失敗談 default_scope編 | TechRacho にあるように、安易にdefault_scopeを使うとscopeの削除がしずらいので思わぬエラーになる可能性があるので注意してください。論理削除などのときに限定するなどしたほうが良いと思います。


参考文献

やりたいことの実現方法が分からなかった、もっとAPIを探してみたい場合に参照して下さい。

以上です。
よく分からない、間違っていることがありましたら、お気軽に以下のコメント欄に記載ください。