Rails3: using Arel to make conditional searches based on conditional params
2010-06-30
I wrote a basic model called Search, I can re-use it for other models that need search features:
# put this in app/models/search.rb
class Search
attr_accessor :attributes
def initialize(attributes = {})
@attributes = attributes
@attributes.each do |k,v|
self.class.send(:define_method,k) { v }
end
end
end
Then I wrote a partial form with params, the following code is only an example, I have more fields:
<div id="searchform">
<%= form_tag search_people_path, :method => 'get' do |f| %>
<div class="field">
<%= label :search, :firstname, 'Firstname' %><br />
<%= text_field :search, :firstname %>
</div>
<div class="field">
<%= label :search, :lastname, 'Lastname' %><br />
<%= text_field :search, :lastname %>
</div>
<%= submit_tag "Search" %> <% end %>
</div>
Finally the controller action called ‘search’. As you can see, the difficult part was that you don’t know which params are passed by the request, so you need to specify conditions only if they are present. Arel helped to resolve this problem:
class PeopleController < ApplicationController
# ... other CRUD actions ...
def search
q = Person.arel_table # initialize Arel table
@people = Person.order(:created_at) # start with a simple option
# initialize Search model with the search params
@search = Search.new(params[:search])
# apply conditions only if they are present
@people = @people.where(q[:lastname].matches("%#{@search.lastname}%")) if @search.lastname
@people = @people.where(q[:firstname].matches("%#{@search.firstname}%")) if @search.firstname
# render index action, it's the same output, just filtered by conditions passed
render :index
end
end
Probably it’s not the best solution, perhaps this could be optimized with some helper, but it works, and helps to figure out how Arel works.