Techioz Blog

Rails で指定されたパラメーターに基づいてクエリするモデル メソッドを作成する方法

概要

私は Rails についてはかなり初心者です。

Flights#index ルートに送信されたパラメータに基づいて、Flight オブジェクトに対してクエリを実行したいと考えています。パラメータは、出発空港 ID、到着空港 ID、および日付です。

ただし、検索フォームは nil 値を URL 内の空の文字列として送信するため (例: params {date: nil) は URL …/flights/index/date=“” として送信されます)。コードを整理するのに苦労しています。 FlightsController を使用し、空ではないパラメーターのみに基づいてクエリを実行するヘルパー メソッドをフライト モデルに記述します。

私の最初の解決策は、パラメータが nil または空の文字列でない場合にのみ、特定のパラメータに基づいてクエリを実行するヘルパー メソッドをモデルに記述することでした。次に、コレクションをビューに渡す前に、コントローラー内のこれらのヘルパー メソッドをすべて文字列化します。例えば:

class Flight < ApplicationRecord
   
   ...

   def self.around_date(date_string, day_interval=1)
    if date_string == "" || !date_string
      return all
    end

    date = Date.parse(date_string)

    lower_date = date - day_interval.days
    upper_date = date + day_interval.days
    where(start_datetime: (lower_date..upper_date))
  end
end
class FlightsController < ApplicationController
  def index
    ...

    if params[:date] || params[:departure_airport_id] || params[:arrival_airport_id]
      @pagy, @flights = pagy(Flight.around_date(params[:date])
                                  .from_departure_airport(params[:departure_airport_id])
                                  .to_arrival_airport(params[:arrival_airport_id])
                                  .order(:start_datetime))
    else
      @flights = nil
    end

これはまさに私が想像していたとおりに機能しました。しかし、これは非常に組織化されておらず、非効率的であるように思えます。そこで、ヘルパー メソッド (#around_date や #from_development_airport など) をプライベートにして、Flight クラスに新しいメソッドを作成しようとしました。

def self.query_search(query_params)
    around_date(query_params[:date], day_interval=0) unless query_params[:date] == ""
    from_departure_airport(query_params[:departure_airport_id]) unless query_params[:departure_airport_id] == ""
    to_arrival_airport(query_params[:arrival_airport_id]) unless query_params[:arrival_airport_id] == ""
end

したがって、モデルではコードを Flight.query_search(query_params) に削減し、強力なパラメーターも使用できます。

これは私にははるかに組織化されているように見えますが、うまくいきません。 query_search メソッドはヘルパー関数からのすべてのクエリを実行するわけではなく、コントローラーは常にメソッドから nil を受け取ります。

私の質問は次の 2 つです。

ありがとう。この質問でうまく表現できなかった点を明確にしていただけると幸いです。

解決策

ActiveRecord.scope の 2 つのコア機能を使用して読みやすさを向上させます。

つまり、params にキーが存在するかどうかをテストする代わりに、その値をスコープに渡し、フィルターを適用する必要があるかどうかをスコープ内でテストします。

# in the model
scope :around_date, ->(date_string, day_interval = 1) {
  if date_string.present?
    date = Date.parse(date_string)

    lower_date = date - day_interval.days
    upper_date = date + day_interval.days

    where(start_datetime: (lower_date..upper_date))
  end
}

そして、次のようにコントローラーでそれを使用します。

@pagy, @flights = pagy(
  Flight.around_date(params[:date])
        .from_departure_airport(params[:departure_airport_id])
        .to_arrival_airport(params[:arrival_airport_id])
        .order(:start_datetime)
)

ところで。 Object#present を使用できますか?オブジェクトが空か空白かを確認するには?