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

必要な人を見つけるときは、存在するすべての条件に AND を使用するのが好きです。もちろん、それは合理的ではなく、DRY ではありません。

私がやろうとしたこと:

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]andparams[: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

私はこのための範囲で広く働きます。デフォルトのスコープから始めて、条件に基づいて他のスコープをマージします。

スコープ (またはクラス メソッド) を記述します。

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 Makes Your Life のスコープオーバーホールセクション

ここで、条件が満たされた場合にのみスコープをマージします。私があなたの条件を理解しているように、特定の検索項目の値はゼロです(年齢が指定されていないなど)、その範囲を検索しないでください。

  ...      
  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 に答える
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 に答える