箱のプログラミング日記。

えんじにあ奮闘記

Railsのスコープの書き方【SQL】

f:id:y_hakoiri:20191102121842j:plain

スコープって何?状態だったところから、使ってみると非常に便利だったのでスコープについて書いておきます。

ちなみにここで言うスコープはメソッドのスコープとかではなく、SQLとのデータのやりとりを簡潔にまとめるときに便利な方のスコープさんです。

スコープとは

スコープを設定することで、関連オブジェクトやモデルへのメソッド呼び出しとして参照される、よく使用されるクエリを指定することができます。

単純なスコープを設定するには、クラスの内部でscopeメソッドを使用し、スコープが呼び出されたときに実行して欲しいクエリをそこで渡します。

引用元:Active Record クエリインターフェイス - Railsガイド

どこに書くのか

データベースとのやりとりなのでモデルに書きます。

Railsで開発をしていると、何をどこに書く問題は慣れるまでは難しいな〜と思います。

私も学習を始めた頃はモジュールとかヘルパーとか使ったことはなく、ビューにロジックを書いたり、コントローラーに複雑なクエリを書いたりとかしてしまってました。

スコープは、コントローラーでやりがちなデータの処理をモデルにまとめて書いておくことでスッキリ見せてくれる役割があります。

どうやって書くのか

Userモデルを例にとります。 ユーザーのステータスは4種類あり、審査中、入会済、停止中、退会済みたいな感じだとして

class User < ApplicationRecord
 scope :active_user, -> { where.not(status: "退会済み") }
end

こうすると、Userモデルに対してこのように使えるようになって

User.active_user

これは以下と同じことになる

User.where.not(status: "退会済み")

(実際はstatusにはenumを使ってるけど、わかりやすく文字列で再現)

要するに、直接モデルに対してwhere句を使ったりするのではなく、

active_userとしてメソッドのようにまとめることで簡潔にわかりやすく書いてるってことですね

何が良いのか

先ほどの例を見ると、なんかそこまで利便性感じないな〜ってなるかもだけど、スコープを使うことで何が良いのか

1.どこからでも呼び出せる

記述量としては多くないからその都度書けば良いじゃん!ということではなく、モデルにこのようなスコープを記述しておくことで、同じような取り出し方を複数のコントローラー等でやりたいときに使い回しができるので便利。

例えばstatusが"退会済み"から"退会"になったとしても、スコープの部分だけ書き換えれば良いので楽ですね。

2.チェーンができる

退会済みかつ他の条件も指定したいな...ってときにはスコープ以外に他の条件もチェーンして検索することができる。

例えば、active_userかつ東京出身のユーザーを検索したい場合はこんな風に書ける

User.active_user.joins(:profiles).where(profiles: {prefecture: "東京"})

(Userと親子関係のProfileモデルに住所を格納しています)

もとい、joins以下も同じように別のスコープとして定義しておけば

scope :from_tokyo, -> { joins(:profiles).where(profiles: {prefecture: "東京"})}

以下のように、複数のスコープをメソッドチェーンのように繋げて検索することができるのでめちゃくちゃ便利。

User.active_user.from_tokyo

うん。スッキリ

パッと見て「どんなユーザーを取り出したいのか」っていうのも想像がつきやすい。

これをスコープなしで書こうとすると、

User.where.not(status: "退会済み").joins(:profiles).where(profiles: {prefecture: "東京"})

これだけ長くなるので、やっぱり便利ですね。

参考

Railsの複雑な検索はスコープを使おう - Qiita

ActiveRecordでOR文を作るときのエラー対処 - hotoolong's blog

Active Record クエリインターフェイス - Railsガイド