0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Railsでスコープを活用してクエリを簡潔に記述する方法

Posted at

Railsでスコープを活用してクエリを簡潔に記述する方法

はじめに

前回の記事では、controllerファイル内で平均難易度が1から3の間で投稿数の多い本を8件取得するコードを作成しました。詳細はこちらの記事をご参照ください。

しかし、今回は平均難易度が2から43から5の場合も同様にデータを取得したいと考えています。愚直に実装すると、同じようなコードが繰り返されてしまい、コードが冗長になります。

@books_dif_low = Book
  .joins(:posts)
  .select('books.*, AVG(posts.difficulty) AS average_difficulty, COUNT(posts.id) AS posts_count')
  .group('books.id')
  .having('AVG(posts.difficulty) BETWEEN ? AND ?', 1, 3)
  .order('posts_count DESC')
  .limit(8)

@books_dif_mid = Book
  .joins(:posts)
  .select('books.*, AVG(posts.difficulty) AS average_difficulty, COUNT(posts.id) AS posts_count')
  .group('books.id')
  .having('AVG(posts.difficulty) BETWEEN ? AND ?', 2, 4)
  .order('posts_count DESC')
  .limit(8)

@books_dif_high = Book
  .joins(:posts)
  .select('books.*, AVG(posts.difficulty) AS average_difficulty, COUNT(posts.id) AS posts_count')
  .group('books.id')
  .having('AVG(posts.difficulty) BETWEEN ? AND ?', 3, 5)
  .order('posts_count DESC')
  .limit(8)

そこで、スコープを活用することで、コードを簡潔かつ再利用可能にする方法を紹介します。

スコープの使い方

スコープとは?

Scope(スコープ)は、RailsのActiveRecordモデル内で定義される、再利用可能なクエリの定義方法です。スコープを使用することで、複雑なクエリをシンプルかつ読みやすく管理できます。

なぜスコープを使うのか?

  1. 再利用性の向上

    同じ条件やロジックを複数の場所で使用する際に、一度スコープとして定義しておけば簡単に呼び出せます。

  2. コードの可読性向上

    複雑なクエリを名前付きのスコープに分割することで、コード全体の理解が容易になります。

  3. チェーン可能

    複数のスコープを組み合わせて柔軟なクエリを構築できます。

スコープの書き方

基本的なスコープの定義方法は以下の通りです。

scope :scope_name, ->(parameters) { where(condition: value) }

次に、具体的なスコープの定義方法を見ていきましょう。

スコープを使った記述

1. with_average_difficulty_between

scope :with_average_difficulty_between, ->(min, max) {
  joins(:posts)
    .select('books.*, AVG(posts.difficulty) AS average_difficulty, COUNT(posts.id) AS posts_count')
    .group('books.id')
    .having('AVG(posts.difficulty) BETWEEN ? AND ?', min, max)
}

役割と機能

このスコープは、平均難易度が指定された範囲(minからmax)にある本を取得します。また、各本の平均難易度投稿数も計算・取得します。

2. ordered_by_posts_count

scope :ordered_by_posts_count, -> {
  order('posts_count DESC')
}

役割と機能

このスコープは、投稿数(posts_count)が多い順に本を並べ替えます。

3. top_ranked

scope :top_ranked, ->(limit = 8) {
  limit(limit)
}

役割と機能

このスコープは、取得する本の数を制限します。デフォルトでは8件の本を取得しますが、引数で指定することも可能です。

Scopeを組み合わせたクエリの実行

定義した3つのスコープをチェーン(連結)することで、目的のデータを簡潔に取得できます。以下に具体例を示します。

# app/models/book.rb
class Book < ApplicationRecord
  has_many :posts

  scope :with_average_difficulty_between, ->(min, max) {
    joins(:posts)
      .select('books.*, AVG(posts.difficulty) AS average_difficulty, COUNT(posts.id) AS posts_count')
      .group('books.id')
      .having('AVG(posts.difficulty) BETWEEN ? AND ?', min, max)
  }

  scope :ordered_by_posts_count, -> {
    order('posts_count DESC')
  }

  scope :top_ranked, ->(limit = 8) {
    limit(limit)
  }
end

これらのスコープを使用して、平均難易度と投稿数に基づいた本を簡単に取得できます。

# コントローラー内の例
@books_dif_low = Book
  .with_average_difficulty_between(1, 3)
  .ordered_by_posts_count
  .top_ranked

@books_dif_mid = Book
  .with_average_difficulty_between(2, 4)
  .ordered_by_posts_count
  .top_ranked

@books_dif_high = Book
  .with_average_difficulty_between(3, 5)
  .ordered_by_posts_count
  .top_ranked

このようにスコープを活用することで、クエリが簡潔になり、メンテナンス性も向上します。

終わりに

スコープを活用することで、冗長なコードを避けることができ、コードの可読性と再利用性が向上します。また、スコープを定義する際にもSQLの基礎知識が大切になると感じました。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?