views:

222

answers:

2

Hello,

I need to select some dynamic price ranges submitted from a search form. How should I approach this with scopes? I am looking for something like this

Painting.price_range(['1..500', '2000..5000'])

SELECT * FROM paintings WHERE price BETWEEN 1..500 **OR** BETWEEN 2000..5000 etc.

Best regards. Asbjørn Morell.

+3  A: 
named_scope :price_range, :conditions => ["(price BETWEEN 1 AND 500) OR (price BETWEEN 2000 AND 5000)"]

OR

named_scope :price_range, :conditions => ["(price ?) OR (price ?)", (1..500).to_s(:db), (2000..5000).to_s(:db)]

Dynamic

named_scope :price_between, lambda { |from, to| { :conditions => ['price > ? AND price <= ?', from, to] } }

named_scope :price_between, lambda { |from, to| { :conditions => ['price BETWEEN ? AND ?', from, to] } }

->

MyModel.price_between(1,100)
Reuben Mallaby
+2  A: 

You'll need to use a lambda on the named_scope. The following should work:

  named_scope :price_range, lambda { |ranges|
    { 
      :conditions => ["(" + 
         ranges.collect {"price between ? and ?"}.join(" or ") + 
                      ")"] +
         ranges.collect {|r| [r.min, r.max]}.flatten 
    }
  }

The first ranges.collect creates as many "between ? and ?" checks as you have ranges and then the second ranges.collect flattens out the ranges and adds them as values to be sanitized into the conditions. I've stuck brackets round the ors just to be on the safe side.

Shadwell
Very nice! Your example works great:Painting.price_range([['1', '200'], ['1000', '2000']]) Painting Load (0.4ms) SELECT * FROM `paintings` WHERE ((price between '1' and '200' or price between '1000' and '2000'))Thanks a lot for handing me the solution on a gold plate like that :)
atmorell
I have a few problems trying to calling the scope from a form:<input type="checkbox" value="[[1, 1000]]" name="search[price_range]" id="search_price_range" checked="checked"/>Parameters: {"commit"=>"Save changes", "search"=>{"price_range"=>"[[1, 1000]]"}}Now the query generated from this is:SELECT * FROM `paintings` WHERE ((price between '[[1, 1000]]' and '[[1, 1000]]')) I think that the problem is that the value from the form is not recognized as an array :/ params[search][price_range].class returns string instead of array. Any suggestions?
atmorell
Yeah, the solution in the answer is for an array of ranges rather than an array of pairs. If you change r.min and r.max for r.first and r.last it should work for pairs too.
Shadwell
Actually, that might not be the answer (though it would work better for arrays). You probably need to change the name of your form attribute to search[price_range][] if you want the value to be an array and remove the outer square brackets on the values. There's a lot of square brackets going on there though!
Shadwell