Scopeについて
モデルには、Scopeというものを定義できる。
Scopeとは、よく利用する検索条件に名前をつけて、ひとまとめにしたもの。
高価なワインだけを取得するためのScope、costlyを定義↓
class Wine < ApplicationRecord
scope :costly, -> { where("price > ?", 5000) }
end
Wine.costly
↑この記述だけで、↓と対価となります。
Wine.where("price > ?", 5000)
メリットは2つ
1.繰り返し利用するクエリの再利用性が上がる
2.クエリに名前をつけることで、可読性が向上
クエリ(Query)とは?
クエリとは、データベースに対して「データを取得したり、追加・更新・削除するための指示(命令)」のこと。
今回の例だと、データベースの winesテーブルから価格が 5000 円以上のワインを取得したい場合、SQLだと次のようなクエリになります。
SELECT "wines".* FROM "wines" WHERE (price > 5000)
Rails では、ActiveRecord を使ってこのクエリを Ruby のコードとして表現できる。
Scopeを使えば、以下のコードだけで表現できます。
Wine.costly
データベースとアプリケーションの橋渡しをするのがクエリの役割で、アプリケーションはクエリを使ってデータを取得したり、更新したりして、それを画面に表示する。
Scopeとクラスメソッドの違い
大きく分けて3つあります。
1.返り値
2.生成されるSQLクエリ
3.チェーン(連結)できるかどうか
Scope
Scopeを利用してWine.costlyを実行すると
必ずActiveRecord::Relationを返します。
sqlクエリにはLIMIT が付かないため、条件に合致するすべてのレコードが取得できます。
SELECT "wines".* FROM "wines" WHERE (price > 5000)
必ずActiveRecord::Relationを返すため、チェーンしてさらに条件を追記できる
Wine.costly.where("name LIKE ?", "%Chateau%")
発行されるSQLクエリ↓
SELECT "wines".* FROM "wines" WHERE (price > 5000) AND (name LIKE '%Chateau%')
クラスメソッド
def self.find_by_price(price)
find_by(price: price)
end
find_byを利用するため、返り値は単一のレコードか、nilになります。(レコードが見つからなければnil)そのため、チェーンで他の条件を追加することはできません。
Wine.find_by_price(5000)
上記を実行すると、
発行されるSQLクエリ↓
SELECT "wines".* FROM "wines" WHERE "wines"."price" = 5000 LIMIT 1
LIMIT 1 が自動的に付加されるため、最初の1件だけが返されるようになっています。
まとめ
Scopeは複数のレコードを対象に条件をチェーンで追加でき、SQLクエリもその条件が全て反映されます。結果がnilとなった場合も該当Scopeの検索条件を除外したクエリを発行し、必ずActiveRecord::Relationを返すという動作をします。
一方、クラスメソッドは単一レコードの取得に特化していて、SQLクエリには自動的に LIMIT 1 が付与され、チェーンもできません。
Scopeで定義する場合とクラスメソッドで定義する場合はこのような違いがあるため、nilを返す必要がある場合はクラスメソッドとして定義する必要があります。
参考文献