views:

48

answers:

3

I built a basic search form that queries one column in one table of my app. I followed episode 37 Railscast: http://railscasts.com/episodes/37-simple-search-form. Note I just posted another search related question, but it's on a completely different issue.

In my app, the search queries the zip code column of my profile table, and returns a list of profiles that contain the right zip code.

Here's my problem. Currently, when a user leaves the input blank and hits the submit button, the search displays all profiles on the site. I don't want this to happen. If the field is blank, I don't want the search to go through. I'd like to either do a flash notice or throw an error, explaining that the user needs to enter a zip code to proceed.

Here's my setup:

PROFILES CONTROLLER

def index  
    @profiles = Profile.search(params[:search])  
end

PROFILE MODEL

def self.search(search)
        if search
                find(:all, :conditions => ['zip LIKE ?', "%#{search}%"])
        else
                find(:all)
        end
end

PROFILE/INDEX.HTML.ERB

<% form_tag ('/profiles', :method => :get) do %>
    <%= text_field_tag :search, params[:search], :maxlength => 5 %>
    <%= submit_tag "Go", :name => nil %>                
<% end %>

Thanks!

A: 

You just need to check if it's blank before you do your search.

def index
  if params[:search].blank?
    flash[:error] = "Doh! You forgot the zip code."
  else
    @profiles = Profile.search(params[:search])
  end
end

If returning all results is never a use case then you might want to remove that code from your model as well. Also if you're checking for a blank search in more than this one controller action you should move the logic into the model.

Andy Gaskell
+1  A: 
def index
  @profiles = Profile.search(params[:search]) unless params[:search].blank?
end

You probably don't want to throw an error if the search field is blank, because the user will see that error the first time he comes to the index page. To properly handle that type of error message, you'll need to do one of several things.

  • Split the form generation and the actual search into two separate actions. In a RESTful app, this would typically be a new and create action. (new for the form, create for the actual search).
  • Add a check for a post, as opposed to a get. Only attempt the search, or throw the error, if it's a post. Otherwise, just show the form. You'll typically see this in older Rails examples (like pre- 2.0 tutorials).
  • Add some hidden field that says "Hey, I'm submitting a search." This is the same idea as checking for a post, but would still work if you wanted all gets for some reason.

My choice would be the first one. It'd roughly look like this.

def new
end

def create
  if params[:search].blank?
    flash.now[:error] = "Please enter a zip code to search for."
    render :new
  else
    @profiles = Profile.search(params[:search])
    render :show
  end
end

In your views, new.html.erb (or .haml or whatever) would contain your search form and show.html.erb would contain your search results. Usually there's a search form partial that both of them would share.

jdl
A: 

I actually found an answer to this that doesn't require me to make any changes to my current setup.

If you look in the search model above, which I copied from the Railscast, you see that Ryan included wildcards on both sides of the search query:

find(:all, :conditions => ['zip LIKE ?', "%#{search}%"])

I'm not familiar with sql syntax at all, so I missed that. It was those wildcards that was letting through the blank search and returning all results in the database. When I removed the "%'s" from that sql statement, the blank search now returns the same result as a search where we don't have a record matching the zip queried, which is the result I wanted.

Just wanted to share so others might catch this in the future.

MikeH
Unfortunately this also reduces the usefulness of your search. Now it will only match whole words, AFAIK.
Josh Matthews
I'm only allowing searches by zip code for right now, so this is exactly what I want.
MikeH