LoginSignup
1
2

Railsにおけるモデルのscopeメソッド

Posted at

はじめに

先日、業務で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

ここでは、PostsControllerindexアクションで、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というスコープを定義しています。

PostsControllerindexアクションでは、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

ここでは、PostsControllerHomeControllerAdmin::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などのエラーを防ぐことができます。

参考文献

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2