Ransack, find record that has all related records

前端 未结 1 1106
刺人心
刺人心 2021-01-04 21:53

I have a recipe model which has_many ingredients and each ingredient belongs to an item. In my advanced search form, I would like a user to select multiple ingredients and l

相关标签:
1条回答
  • 2021-01-04 21:59

    I recently came across similar task for my project (Rails 4.2.4 / Ruby 2.3.1).

    Estate has many comforts. I need to get all estates, which include all selected comforts.

    Here is how I resolved it using Ransack

    In my case I have has_many :through relation.

    estate.rb

    class Estate < ActiveRecord::Base
      has_many :estate_comforts
      has_many :comforts, through: :estate_comforts
    end
    

    comfort.rb

    class Comfort < ActiveRecord::Base
      has_many :estate_comforts
      has_many :estates, through: :estate_comforts
    end
    

    estate_comfort.rb

    class EstateComfort < ActiveRecord::Base
      belongs_to :estate
      belongs_to :comfort
    end
    

    For complex queries you'll need to do search via post. For that you have to edit routes like this. And add search action to estates_controlle.rb. For more info read Ransack wiki

    routes.rb

    ...
    resources :estates
      collection do
        match 'search' => 'estates#search', via: %i[get post], as: :search
      end
    end
    

    estates_controller.rb

    class EstatesController < ApplicationController
      ...
    
      def index
        @q = Estate.ransack(params[:q])
    
        @estates =
          if params[:q]&.has_key?(:estate_comforts_comfort_id_eq_any)
            # Store checked comforts
            session[:estate_comforts_comfort_id_eq_any] = params[:q][:estate_comforts_comfort_id_eq_any]
    
            comforts_count = params[:q][:estate_comforts_comfort_id_eq_any].count
            ids = @q.result.includes(:estate_comforts).group_by(&:id).select { |_, v| v.count == comforts_count}.keys
            Estate.where(id: ids)
          else
            @q.result(distinct: true)
          end
      end
    
      def search
        index
        render :index
      end
    end
    

    And finally the template portion...

    estates/index.haml

    = search_form_for @q, url: search_estates_path, html: { method: :post } do |f|
      # here goes the form inputs
    
      # Polulate checkboxes with previously checked comforts 
      - Comfort.find_each do |comfort|
        # Was checked previously?
        - checked = comfort.id.to_s.in?(session[:estate_comforts_comfort_id_eq_any].to_a)
        %div  
          %input{ name: 'q[estate_comforts_comfort_id_eq_any][]',
                  type: "checkbox",
                  id: "checkbox#{comfort.id}",
                  value: comfort.id,
                  checked: checked }
            %label{for: "checkbox#{comfort.id}"}= comfort.name
    

    Will generate following html

    <form class="estate_search" id="estate_search" action="/estates/search" accept-charset="UTF-8" method="post">
      <div>
        <input checked="" id="checkbox1" name="q[estate_comforts_comfort_id_eq_any][]" type="checkbox" value="1">
        <label for="checkbox1">Comfort Name 1</label>
      </div>
      <div>
        <input id="checkbox2" name="q[estate_comforts_comfort_id_eq_any][]" type="checkbox" value="2">
        <label for="checkbox2">Comfort Name 2</label>
      </div>
    </form>

    0 讨论(0)
提交回复
热议问题