はじめに
先日、業務でscope
メソッドでの実装したので、備忘録として記事にしたいと思います。
この記事では、scope
メソッドの基本的な使い方をまとめてみました。
モデルのscopeメソッドとは?
scope
メソッドは、よく使用されるクエリをメソッドとして定義することができる機能です。
これにより、複雑なクエリを簡単に再利用できるようになります。
Scopeメソッドの基本構文
scope
メソッドの基本的な構文は以下のようになります。
scope :scope_name, -> { query }
-
scope_name
: スコープの名前を指定します。 -
query
: 実行するクエリを指定します。
Scopeメソッドの使用例
今回は、記事の一覧表示で人気の記事を取得する処理を例に見ていきます。
scopeを使わないパターン
scope
を使わずに人気の記事を取得する場合、以下のようにクエリを書くことになります。
# app/models/post.rb
class Post < ApplicationRecord
end
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
def index
@posts = Post.where('likes_count > ?', 100).order(created_at: :desc).page(params[:page]).per(10)
end
end
ここでは、PostsController
のindex
アクションで、likes_count
が100を超える記事を取得し、created_at
の降順で並び替え、ページネーションを行っています。
scopeを使うパターン
次に、scope
を使って人気の記事を取得する場合です。
# app/models/post.rb
class Post < ApplicationRecord
scope :popular, -> { where('likes_count > ?', 100).order(created_at: :desc) }
end
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
def index
@posts = Post.popular.page(params[:page]).per(10)
end
end
ここでは、Post
モデルにpopular
というスコープを定義しています。
PostsController
のindex
アクションでは、Post.popular
という形でpopular
スコープを呼び出し、人気の記事を取得しています。
複数のコントローラーで使用できる例
scope
を使うことで、複数のコントローラーで同じクエリを再利用できます。
# app/models/post.rb
class Post < ApplicationRecord
scope :popular, -> { where('likes_count > ?', 100).order(created_at: :desc) }
end
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
def index
@posts = Post.popular.page(params[:page]).per(10)
end
end
# app/controllers/home_controller.rb
class HomeController < ApplicationController
def index
@popular_posts = Post.popular.limit(5)
end
end
# app/controllers/admin/posts_controller.rb
class Admin::PostsController < ApplicationController
def index
@posts = Post.popular.page(params[:page]).per(20)
end
end
ここでは、PostsController
、HomeController
、Admin::PostsController
の3つのコントローラーでpopular
スコープを使用しています。
Post.popular
という形でpopular
スコープを呼び出すだけで、人気の記事を取得できます。
返り値について(ActiveRecord::Relation)
scope
メソッドは、ActiveRecord::Relation
オブジェクトを返します。
これによってクエリメソッドをチェーンして使用できるメリットがあります。
# app/models/post.rb
class Post < ApplicationRecord
scope :popular, -> { where('likes_count > ?', 100).order(created_at: :desc) }
end
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
def index
@posts = Post.popular.where(category: 'Ruby').page(params[:page]).per(10)
end
end
popular
スコープの後にwhere
メソッドをチェーンして使用しています。
これにより、人気の記事の中からカテゴリが「Ruby」の記事のみを取得することができます。
ActiveRecord::Relation
オブジェクトを返すことで、このようにクエリメソッドを柔軟に組み合わせることができます。
nilの場合について(scopeとclassメソッドの違い)
scope
メソッドと通常のクラスメソッドの大きな違いの一つは、nilを返す場合の動作です。
# app/models/post.rb
class Post < ApplicationRecord
# scope
scope :popular, -> { where('likes_count > ?', 100).order(created_at: :desc) }
# classメソッド
def self.popular_class_method
where('likes_count > ?', 100).order(created_at: :desc)
end
end
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
def index
# scopeを使用した場合
@posts = Post.popular.where(category: 'Ruby').page(params[:page]).per(10)
# classメソッドを使用した場合
@posts_class_method = Post.popular_class_method.where(category: 'Python').page(params[:page]).per(10)
end
end
ここでは、popularスコープとpopular_class_methodクラスメソッドを定義しています。
popular スコープ |
popular_class_method クラスメソッド |
|
---|---|---|
条件に一致する記事がない場合の戻り値 | 空のActiveRecord::Relationオブジェクト | nil |
コントローラー内の変数への代入 | @posts変数には空のオブジェクトが代入される | @posts_class_method変数にはnilが代入される |
ビュー表示 | エラーなく空のビューが表示される | NoMethodErrorが発生する |
scope
メソッドを使用することで、常にActiveRecord::Relation
オブジェクトを返すことができます。
これによって、scope
メソッドで定義したスコープに対して、メソッドチェーンを常に使えるのがメリットです。
また、ActiveRecord::Relation
オブジェクトを返すことで、意図しないNoMethodError
などのエラーを防ぐことができます。
参考文献