Поиск нескольких возможных значений параметров - PullRequest
0 голосов
/ 16 февраля 2020

На данный момент у меня есть форма, в которой пользователь может ввести цену за человека и / или продолжительность и / или team_size. То, что я хотел бы сделать sh, - это извлечь все записи из таблицы, которые соответствуют пользовательскому вводу, и для этого я установил область действия в модели:

scope :filter_by_team_size, -> (team_size) { where("team_size = ?", team_size) }
scope :filter_by_duration, -> (duration) { where("duration = ?", duration) }
scope :filter_by_price, -> (price) { where("price = ?", price) }

И затем в контроллере снова используйте это для получить записи, выполнив следующие действия:

@experiences = policy_scope(Experience).order(team_size: :desc).geocoded.filter_by_team_size(params[:team_size]) if params[:team_size].present?
@experiences = policy_scope(Experience).order(duration: :desc).geocoded.filter_by_duration(params[:duration]) if params[:duration].present?
@experiences = policy_scope(Experience).order(price: :desc).geocoded.filter_by_price(params[:price]) if params[:price].present?

Однако это дает мне только те записи, для которых соответствует первое входное значение, но игнорирует все остальные значения. Кроме того, когда вы просматриваете результаты поиска и снова используете фильтр, он должен применять этот фильтр только для уже найденных записей.

Любое предложение о том, как решить эту проблему, будет высоко оценено!

1 Ответ

0 голосов
/ 16 февраля 2020

Один из способов справиться с этим - использовать виртуальную модель, которая обрабатывает параметры привязки к форме и из формы:

class SearchQuery
  include ActiveModel::Model
  include ActiveModel::Attributes
  attribute :team_size, :integer
  attribute :duration
  attribute :price
end

Затем можно настроить форму:

<%= form_with(model: (@search_query || SearchQuery.new), url: '/experiences', method: :get) %>
  <div>
    <%= f.label :team_size %>
    <%= f.number_field :team_size %>
  </div>
  # ..
<% end %>

И затем вы можете просто привязать параметры к модели с помощью ActionController::Parameters#permit, как если бы вы делали это с обычной моделью ActiveRecord:

class ExperiencesController
  before_action :set_search_query, only: :index, if: ->{ params[:search_query].present? }
  # ...

  def index
    @experiences = if @search_query 
      @search_query.build_scope(policy_scope(Experience))
    else
      policy_scope(Experience)
    end.geocoded
  end

  private
  def set_search_query
     @search_query = SearchQuery.new(search_query_params)
  end

  def search_query_params
     params.fetch(:search_query).permit(:team_size, :duration, :price)
  end
end

Эта петля сделает форму с состоянием точно такой же, как ваши обычные формы CRUD. На самом деле мы не реализовали #build_scope да, поэтому давайте сделаем так:

class SearchQuery
  include ActiveModel::Model
  include ActiveModel::Attributes
  attribute :team_size, :integer
  attribute :duration
  attribute :price

  def build_scope(base_scope)
    compacted_attributes = attributes.reject { value.nil? || value.empty? }
    compacted_attributes.each_with_object(base_scope) do |(k,v), base|
      if base.respond_to? "filter_by_#{k}"
        # lets you customize the logic with a scope
        base.send("filter_by_#{k}", v) # the scope is responsible for ordering
      else
        # convention over configuration!
        base.where(Hash[k,v]).order(Hash[k,:desc])
      end
    end
  end
end

Поскольку это использует соглашение о конфигурации, вы можете избавиться от этих бессмысленных областей в вашей модели.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...