Rails – RansackでOR条件グループを適用させる

やりたいこと

Ransackの検索条件の一部にOR条件グループを作りたい。

例えば、以下のようなテーブル構成の場合、

テーブル名カラム名
a_tablesprice
fee
b_tablescost
a_table_id

Ransack の検索条件の一部として、次のようなOR条件を適用させたい:

  • a_tables の 「price」 が入力されており、かつ
  • a_tables の 「fee」 または b_tables の「cost」が入力されている

実装のポイント

このような複雑な条件指定をRansackのデフォルトで表現するのは難しい

なので、Viewsの検索フォームでは「任意の値」をラジオボタンで選択させ、

コントローラで受け取った値をカスタマイズし、「OR条件グループ」が適用されるようにする

OR条件グループの表現

検索条件グループを作りたいときは、「g」という key に連番 key をネストさせて、ハッシュパラメータを渡せばよい。


custom_q_0 = params[:q]
custom_q_1 = { price_not_null: true, fee_not_null: true }
Model.ransack(g: { '0': custom_q_0, '1': custom_q_1 })

そして、検索条件グループ内を「OR条件」にしたいときは、ハッシュパラメータに「m: ‘or’」を追加する。


custom_q_0 = params[:q]
custom_q_1 = { price_not_null: true, fee_not_null: true, m: 'or' }
Model.ransack(g: { '0': custom_q_0, '1': custom_q_1 })

実装例

Views

ラジオボタンで検索パラメータのカスタムに必要な「任意の値」を選択させる。


# slim
  = search_form_for @q, url: a_tables_path, do |f|
    table
      tr
        td
          label = '原価入力'
        td.form-inline
          .form-check.form-check-inline.ml-3
            label.form-check-label.mb-1
              = radio_button :custom_search,
                :cost_entries,
                "all",
                { checked: @cost_entries_all, class: 'mr-2' }
              = '全て'
            label.form-check-label.mb-1
              = radio_button :custom_search,
                :cost_entries,
                "price_and_cost_present_true",
                { checked: @cost_entries_present, class: 'mr-2 ml-3' }
              = '入力済'
            label.form-check-label.mb-1
              = radio_button :custom_search,
                :cost_entries,
                "price_and_b_tables_cost_not_null",
                { checked: @cost_entries_blank, class: 'mr-2 ml-3' }
              = '未入力'
  # 以下、省略

Controllers

受け取った値を「OR条件グループ」が適用されるようにカスタマイズする。


class ATablesController < ApplicationController
  def index
    @q = ATable.includes(:b_tables).ransack(custom_search_q)
    @a_tables = @q.result
  end

  private

  def custom_search_q
    q_hash = params[:q].to_unsafe_hash
    case q_hash[:custom_search][:cost_entries]
    when 'price_and_cost_present_true' # OR条件がYesの場合
      custom_or_q = {
        price_and_b_tables_cost_not_null: true,
        price_and_fee_not_null: true,
        m: 'or'
      }
      @cost_entries_present = true
    when 'price_or_cost_null_true'  # OR条件がNoの場合
      custom_or_q = {
        price_and_b_tables_cost_not_null: false,
        price_and_fee_not_null: false,
        b_tables_cost_and_fee_null: true,
        m: 'or'
      }
      @cost_entries_blank = true
    else # 以外、全て
      custom_or_q = {}
      @cost_entries_all = true
    end
    q_hash.merge(g: { '0': custom_or_q })
  end
end

Sources

兵庫県西宮市生まれのフリーランスRailsエンジニア。海外を拠点にデジタルノマド生活中。/ 前職・資格:公認会計士 / プログラミング言語:Ruby, JavaScript, HTML, CSS / 日本語・英語
コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です