2

RoR 中有一个搜索操作可以处理一些参数,例如:

params[:name] # can be nil or first_name
params[:age]        # can be nil or age 
params[:city]    # can be nil or country
params[:tag]    # can be nil or country

型号名称是Person。它也has_many :tags

在寻找我需要的人时,我喜欢和所有存在的条件。当然,它不合理也不干燥。

我试图做的事情:

conditions = []
conditions << [ "name like ?", params[:name]+"%" ] if params[:name].present?
conditions << [ "age = ?", params[:age] ] if params[:age].present?
conditions << [ "city = like ?", params[:city]+"%" ] if params[:city].present?
@persons = Person.all(:conditions => conditions )
#What about tags?  How do include them if params[:tag].present?

当然,我希望我的代码是 DRY。现在不是了。params[:age]更重要的是,如果并且不存在params[:name],它将导致异常。params[:city]

我该如何解决?以及如何包含由tag.name=params[:tag](if params[:tag].present?) 过滤的人员的标签?

4

3 回答 3

0

要在查询中包含标签条件:

if params[:tag].present?
  @persons = Person.all(:conditions => conditions).includes(:tags).where("tags.name = ?", params[:tag])
else
  @persons = Person.all(:conditions => conditions)
end

至于当 params[:age] .. 不存在时出现的错误 - 这很奇怪,因为如果未在 params 中设置密钥,它应该返回 false。你能粘贴你得到的错误吗?

于 2012-09-04T15:02:11.047 回答
0

你应该这样做:

假设过滤器参数包括:

filters = {
  :name_like => "Grienders",
  :age_equal => 15
}

现在你为每个定义方法

class Person
  def search_with_filters(filters)
    query = self.scoped
      filters.each do |key, values|
        query = query.send(key, values)
      end
    return query
  end

  def name_like(name)
    where("name like ?", name)
  end

  def age_equal(age)
    where(:age => age
  end
end

您是否看到方法 search_with_filters 将是“控制”方法,它将采用一组查询条件(name_like、age_equal 等)并使用 send 方法将它们传递给匹配的方法名称,同时我们还传递将成为方法参数的条件。这种方式之所以好的原因是因为您可以扩展到任意数量的条件(您的过滤器可以说变得很大)并且代码非常干净,因为您所要做的就是填充您的过滤器参数。该方法可读性强,模块化且易于测试

于 2012-09-05T16:40:08.280 回答
0

我会为此广泛使用范围。从默认范围开始,根据条件合并其他范围。

编写范围(或类方法):

class Person << AR::Base
  ...
  NAME_PARTS = ['first_name', 'last_name']
  scope :by_name, lambda { |q| where([NAME_PARTS.join(' LIKE :q OR ') + ' LIKE :q', { :q => "%#{q}%" }]) }
  scope :by_email, lambda { |q| joins(:account).where(["accounts.email LIKE :q", { :q => "%#{q}%" }]) }
  scope :by_age, where(["people.age = ?", q])
  scope :tagged, lambda { |q| joins(:tags).where(["tags.name LIKE :q", { :q => "%#{q}%" }]) }
end

参考:

  1. Rails 3 中的作用域
  2. Rails 3 如何让您的生活更美好中的范围大修部分

现在,仅在满足条件时才合并范围。据我了解您的情况,特定搜索项的值是否为零(例如,未给出年龄),请勿搜索该范围。

  ...      
  def search(object)
    interested_fields = ['name', 'age', 'email', 'tags']
    criteria = object.attributes.extract!(*interesting_fields) # returns { :age => 20, ... }

    scope = object.class.scoped

    build(criteria).each do |k, v|
      scope = scope.send(k.to_sym, v)
    end

    scope.all
  end

  # This method actually builds the search criteria.
  # Only keep param which has value and reject the rest.
  def build(params)
    required = ['name', 'age', 'email', 'tags']
    params.delete_if { |k, v| required.include?(k) && v.blank? }

    params
  end
...

参考:

  1. 提炼!
于 2012-09-05T19:17:25.490 回答