在多个参数后搜索,Ruby on Rails

Search after multiple parameters, Ruby on Rails

我在 Rails 上对 Ruby 很陌生,我正在尝试创建一个允许用户同时搜索多个参数的搜索功能;从,到。需要记住的是,在开发的后期可能会有更多的参数。我在搜索其中一个字段时可以使用它,但仅此而已。

搜索视图:

<%= form_tag(journeys_path, :method => "get", from: "search-form") do %>
    <%= text_field_tag :search_from, params[:search_from], placeholder: "Search from", :class => 'input' %>
    <%= text_field_tag :search_to, params[:search_to], placeholder: "Search to", :class => 'input' %>
    <%= submit_tag "Search", :class => 'submit' %>
  <% end %>

方法:

class Journey < ActiveRecord::Base
  def self.search(search_from)
    self.where("from_place LIKE ?", "%#{search_from}%")
  end
end

控制器:

class JourneysController < ApplicationController

  def index
    @journeys = Journey.all
      if params[:search_from]
        @journeys = Journey.search(params[:search_from])
      else
        @journeys = Journey.all.order('created_at DESC')
    end
  end

  def search
    @journeys = Journey.search(params[:search_from])
  end
end

我已经尝试了一些我在其他问题中找到的精华和各种解决方案,但我在 RoR 方面还不够好,无法在没有帮助的情况下成功地正确应用它们。如果能得到任何帮助,我将不胜感激。

谢谢!

型号:

class Journey < ActiveRecord::Base
  def self.search(search_from, search_to)
    self.where("from_place LIKE ? and to_place LIKE ?", "%#{search_from}%", "%#{search_to}%")
  end
end

控制器:

class JourneysController < ApplicationController

  def index
      if params[:search_from] and params[:search_to]
        @journeys = search
      else
        @journeys = Journey.all.order('created_at DESC')
    end
  end

  def search
    @journeys = Journey.search(params[:search_from], params[:search_to])
  end
end

这里最好的方法是将您的搜索表单封装为一个单独的 Ruby class。在此处使用 Virtus 有助于免费获得类型强制转换。

class SearchForm
  include Virtus.model # Our virtus module
  include ActiveModel::Model # To get ActiveRecord-like behaviour for free.

  attribute :from, String
  attribute :to, String

  # Just to check if any search param present, 
  # you could substitute this with validations and just call valid?
  def present?
    attributes.values.any?{|value| value.present?}
  end

```

在 Rails 3 IIRC 中,您还必须包括 ActiveModel::Validations 以便能够在需要时验证您的表单输入。 现在,让我们看看如何重构控制器。我们从参数中实例化表单对象并将其传递给模型查询方法以获取所需的记录。我还移出了 if 子句的排序并使用了符号排序参数 - 更清洁的 IMO。

def index
  @search_form = SearchForm.new(search_params)
  if @search_form.valid? && @search_form.present?
    @journeys = Journey.search(@search_form)
  else
    @journeys = Journey.all
  end
  @journeys = @journeys.order(created_at: :desc)
end

def search
  @journeys = Journey.search(SearchForm.new(search_params)
end

private
def search_params
  params.require(:search_form).permit(:from, :to)
end

现在进入视图:form_for 将与我们的表单对象完美配合,simple_form_for

也是如此
<%= form_for @search_form, url: journeys_path, method: :get do |f| %>
  <%= f.text_field :from, placeholder: "Search from", class: 'input' %>
  <%= f.text_field :to, placeholder: "Search to", class: 'input' %>
  <%= f.submit "Search", class: 'submit' %>
<% end %>

视图现在看起来更短更清晰。在对象中封装参数使得使用搜索参数 muuuuch 更容易。

型号:

class Journey < ActiveRecord::Base
  def self.search(search_form)
    if search_form.kind_of?(SearchForm)
      journeys = all # I'm calling Journey.all here to build ActiveRecord::Relation object
      if search_form.from.present?
        journeys = journeys.where("from_place LIKE ?", "%#{search_form.from}%")
      end
      if search_form.to.present?
        journeys = journeys.where("to_place LIKE ?", "%#{search_form.to}%")
      end
    else
      raise ArgumentError, 'You should pass SearchForm instance to Journey.search method'
    end
  end
end

请注意我是如何通过调用 Journeys.all 并应用每个搜索参数(如果存在)来构建 ActiveRecord::Relation 对象的。像这样链接 where 会自动将 AND 放在中间,如果你需要 OR Rails 4 有它:Journey.or(condition)。 这种方法的优点:

  1. 您使用的是 Plain Old Ruby 类,这里几乎没有魔法,它在很多方面都像通常的 Rails 模型一样工作。将搜索参数放入对象中可以更轻松地重构代码。 Virtus 是唯一的依赖项,当然没有 Rails 本身,它更多的是为了方便和避免编写无聊的样板代码。
  2. 如果需要,您可以轻松验证输入(如果您真的想对输入严格并显示用户验证错误,而不是默默地执行具有矛盾条件且不返回任何结果的愚蠢查询)。