モデルのscope機能とは
モデル側であらかじめ特定の条件式に対して名前をつけて定義し、その名前でメソッドの様に条件式を呼び出すことが出来る仕組みのこと。
実装例
現在時刻よりも過去の記事を取得したい場合、過去のように記載する。
scope :past_published, -> { where('published_at <= ?', Time.current) }
定義したスコープは以下のように呼び出す事が出来る。
Article.past_published
scope機能のメリット
1.条件式に名前を付けられるので、直感的なコードになる
2.修正箇所を限定することが出来る
3.コードが短くなる
scope機能の基本的な使い方
scopeメソッドの第一引数に条件式を呼び出すための名前、第二引数に条件式を実装するlambdaを渡す。
class モデル名 < ApplicationRecord
scope :スコープの名前, -> { 条件式 }
end
冒頭での例がこれに該当する。
第一引数は、シンボル:を使ってスコープの名前を指定。
第二引数の->{条件式}のlambdaは、処理の中でメソッドを定義してくれる手法。
※第二引数のlambdaは、->{}の中に条件式を記述することが出来るとだけ覚えておく。
引数を使う方法
scopeメソッドは、下記の様に->の後に引数を定義することで、引数を渡すことが出来る。
class モデル名 < ApplicationRecord
scope :スコープの名前, -> (引数){ 条件式 }
end
公開記事を取得する上限を2件までにしたい場合、下記のように記載できる。
取得する記事の上限をその都度変更したい場合は、下記の様に引数countをscopeメソッドに定義する
# 引数を使用しない場合
class Blog < ApplicationRecord
scope :published, -> { where(published: true).limit(2) }
end
# 引数を使用した場合
# 取得する記事の上限をその都度変更したい場合はこっち
class Blog < ApplicationRecord
scope :published, -> (count){ where(published: true).limit(count) }
end
条件分を使う方法
scopeメソッドは、if文を使って処理を分岐することが出来る。
下記は、引数で渡される値が5未満の場合はその値の記事数を取得する事が出来て、5を超える場合は5件以上はないので全件(5件)取得する例。
class Blog < ApplicationRecord
scope :published, -> (count){ where(published: true).limit(count) if count < 5 }
end
scope機能の応用
nilの場合について
scopeメソッドは、scopeメソッドで定義した条件式がnilを返す場合は、allメソッドを実行する。
またallメソッドの返り値はActiveRecord::Relationオブジェクトなので、scopeメソッドで定義した条件式がnilを返す場合でも、ActiveRecord::Relationオブジェクトを返す。
# blogsテーブルの全レコードが取得される
class Blog < ApplicationRecord
scope :published, -> { nil } # 条件式をnilに変更
end
スコープとクラスメソッドの違い
クラスメソッドでも同じように定義可能。対象モデル.クラスメソッド名で定義した条件式を呼び出す事が出来る。
class モデル名 < ApplicationRecord
def self.メソッド名
条件式
end
end
相違点は、scopeメソッドとクラスメソッドは、nilを返す時の挙動が違う。
・scopeメソッド
nilの場合にallメソッドが実行されるので、メソッドチェーンを使う事ができる。
・クラスメソッド
nilの場合はnilが返るので、メソッドチェーンを使う事が出来ない。
scopeメソッドの引数は、クラスメソッドの機能を複製させたものなので、引数を使う場合はクラスメソッドを使う方が推奨される。