はじめに
has_manyで使われるクエリをカスタマイズしたかったので、その方法を調べたところ、初めて見る記述だったので、調べてみました。
以下に例を示します。Postに関連するlikeを作られた逆順(新しい順)で取得するための記述です。
class Post < ApplicationRecord
has_many :likes, -> { order(created_at: :desc) }
end
has_manyのスコープ
Railsガイドでは、次のように説明されています。
has_manyで使われるクエリをカスタマイズしたい場合があります。スコープブロックを用いてこのようなカスタマイズを行えます。
つまり、この以下の記述は、スコープブロックというものということです。
-> { order(created_at: :desc) }
modelのスコープメソッド
ActiveRecordの機能の一つにscopeメソッドというものがあります。
よく使うクエリをスコープに設定すると、関連オブジェクトやモデルへのメソッド呼び出しとして参照できるようになります。スコープでは、where、joins、includesなど、これまでに登場したメソッドをすべて使えます。どのスコープメソッドも、常にActiveRecord::Relationオブジェクトを返します。
scopeメソッドの基本形
class Article < ApplicationRecord
scope :スコープ名, -> { 条件式 }
end
例
class Article < ApplicationRecord
scope :published, -> { where(published: true) }
end
publishedというscopeを作成しています。矢印の後で、のスコープが行う処理をブロックで記述します。
今回の場合、articlesテーブルからpublishedカラムがtrueのデータを取得するというscopeとなります。
scopeメソッドの使い方
モデル.scope
@articles = Article.published
#=>@articlesには、publishedカラムがtrueのレコードが全て代入される。
scopeメソッドに引数を渡す
class Book < ApplicationRecord
scope :costs_more_than, ->(amount) { where("price > ?", amount) }
end
@books = Book.costs_more_than(100.10)
#=>booksテーブルからpriceが100.10より大きいレコードを取得して変数に代入します。
where("price > ?", amount)
の部分の"?"は、プレースホルダーというもので、第二引数の値が入ります。
参考:【Rails】 whereメソッドを使って欲しいデータの取得をしよう!
scopeメソッドを使用するメリット
- 条件式に名前を付けられるので直感的なコードになる
- 共通化により修正箇所が少なくて済む
- 記述コードが短くなる
"->{}"はいったいなに?
"->{}"は、lambdaというものです。
lambdaとは
無名関数のことです。
関数を表現する式に文字ラムダ (λ) を使うという慣習からその名がある。
引用:ラムダ計算
名前の由来は、慣習によるものみたいです
無名関数とは
その名のとおり「名前のない関数」のことです。その正体は、RubyのProcオブジェクトです。
nameless_func = lambda { |n| n**2 }
nameless_func.(5)
# => 25
->(アロー関数)
->(アロー関数)はlambdaのリテラルです。
そのため、lambdaの部分を->(アロー関数)に置き換えることができます。
nameless_func = -> (n) { n**2 }
nameless_func.(5)
# => 25
[補足]RubyのProcオブジェクトとは
ブロックそれ自体は、ただの処理のかたまりで、オブジェクトではありません。そのため、Procオブジェクトとしてインスタンス化する必要があります。
参考:class Proc
まとめ
scopeメソッドについて
class Article < ApplicationRecord
scope :published, -> { where(published: true) }
end
lambdaの部分が無名関数。つまり、名前の無い関数。
scopeメソッドは、無名関数にスコープ名を付すことで、使い回しができるようにしたものだったのですね。
has_manyのスコープについて
has_manyで使われるクエリをカスタマイズしたい場合は、スコープブロック(アロー関数を用いた無名関数)を用いてカスタマイズを行えます。
class Post < ApplicationRecord
has_many :likes, -> { order(created_at: :desc) }
end
has_many(関連モデル名, scope=nil, オプション引数)
参考
Active Record の関連付け
【Rails】 orderメソッドを使って取得したデータを並び替えよう!
Active Record クエリインターフェイス
[Rails]モデルのscopeメソッド
【Railsのモデルで使用】scopeのlambdaとは何なのか?使い方を紹介
class Proc