【问题标题】:Search after multiple parameters, Ruby on Rails搜索多个参数,Ruby on Rails
【发布时间】:2015-03-18 09:21:47
【问题描述】:

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

搜索视图:

<%= 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 方面还不够好,还没有在没有帮助的情况下成功地正确应用它们。如果能得到任何帮助,我将不胜感激。

谢谢!

【问题讨论】:

标签: ruby-on-rails ruby search


【解决方案1】:

型号:

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

【讨论】:

    【解决方案2】:

    这里最好的方法是将您的搜索表单封装为一个单独的 Ruby 类。在此处使用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 才能在需要时验证表单输入。 现在,让我们看看如何重构控制器。我们从 params 实例化表单对象并将其传递给模型查询方法以获取所需的记录。我还将排序移出 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 %>
    

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

    型号:

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

    【讨论】:

    • 很好的答案!我尝试了上面两个人发布的简短解决方案,但绝对会看看我是否可以根据您的想法使我的代码看起来更好,谢谢!
    • 我的答案范围之外的内容是分页。在生产中始终通过分页限制您的查询非常重要 - 如果用户不填写表单字段,您将以 Journey.all 结尾。现在想象表中有 100000 条记录,服务器将消耗一些 1gb 的内存来服务这个请求。我推荐 kaminari 进行分页,因为它生成的 html 标记非常简单和灵活。
    猜你喜欢
    • 2011-12-17
    • 2012-07-19
    • 2017-06-15
    • 1970-01-01
    • 1970-01-01
    • 2012-07-21
    • 2010-11-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多