Creating site-wide search in a Rails app - ruby-on-rails

I'm trying to create a search form that retrieves results based on a user's query for a restaurant's name. So far I've setup its route, controller, and index view.
routes.rb
resources :search, :only => [:index]
search_controller.rb
class SearchController < ApplicationController
def index
if params[:query].present?
#restaurants = Restaurant.search(params[:query])
else
#restaurants = Restaurant.all
end
end
end
search/index.html.erb
<% #restaurants.each do |restaurant| %>
<%= restaurant.name %>
<% end %>
Here is how the search for is setup:
layouts/_header.html.erb
<%= form_for search_index_path, method: :get do |f| %>
<%= text_field_tag :query, params[:query] %>
<%= submit_tag "Search", name: nil %>
<% end %>
Right now I'm running into two problems. The first being that if I enter a query and submit, the page doesn't go to the index page. All it does is append the query to the current page I'm on:
localhot:3000/restaurant?utf8=✓&query=pizza
Second is that I'm getting every restaurant in my db on the index page (as expected). Is there a way that I can make it so the page is blank for anything other than on search requests?

Question 1
Use form_tag instead of form_for, since the latter is used to handle specific model objects and this is not the case.
Question 2
You can achieve that by:
if params[:query].present?
#restaurants = Restaurant.search(params[:query])
else
#restaurants = [] # or Restaurant.none if you need a chainable scope
end

Related

Using ransack to search from different controller action

I have a PagesController that contains two actions
def search
#q = Listing.ransack(params[:q])
#listings = #q.result(distinct: true)
#listings = #listings.where(active: true).order("created_at DESC").page(params[:page]).per(12)
end
and
def home
end
now what I would like to achieve is that I could fire a search from the home path to the search path like
home.html.erb
<%= search_form_for #q, url: search_path do |f| %>
<%= f.search_field :listing_name_cont %>
<%= f.submit 'Search' %>
but this returns No Ransack Search object was provided to search_form_for which It should since there is no q param passed to the home action. The only way I can get this to work is that if I modify the home action as follows
def home
#q = Listing.ransack(params[:q])
end
but this doesn't seem reasonable since I'm not displaying any of the listings on the home page so I can imagine this will just slow down the page by quite a lot If I'm fetching a lot of listings. Is there any other way I could achieve this?
You can do <%= search_form_for #q, url: 'home' do |f| %>
'home' can be whatever you want the URL extension to be.

How to render search results only when the user press search

Does anyone know how to only display search results once you have clicked the search button?
At the moment, my page is currently displaying everything from my #flights.each. But I only want this information to become visible once they have clicked search :)
my index.html.erb
<h1>Flights#index</h1>
<p>Find me in app/views/flights/index.html.erb</p>
From Airport:
<%= form_for(flights_index_path, method: :get) do %>
<%= text_field_tag :from_airport_id, params[:from_airport_id] %>
<%= submit_tag 'Search' %>
<% end %>
<% #flights.each do |f| %>
<br>
<br>
Flight from <%= f.from_airport_id %>
Arriving at <%= f.to_airport_id %>
<% end %>
my controller
class FlightsController < ApplicationController
def index
if params[:from_airport_id]
#flights = Flight.where('from_airport_id LIKE ?', "%#{params[:from_airport_id]}%")
else
#flights = Flight.all
end
end
private
def flights_path
params.require(:flight).permit(:flight, :from_airport_id)
end
end
I believe it's because you have #flights = Flight.all in the else condition. When there is no query(such as after hitting the submit button and passing over the params) it will default to show all the flights. I'd take this line out and only have
def index
if params[:from_airport_id]
#flights = Flight.where('from_airport_id LIKE ?', "%#{params[:from_airport_id]}%")
else
#flights = []
end
end
Or you can look to have an AJAX request from your flights search and render the form that way.

search form in nav bar doesn't render anything

I have a search form in my nav bar
<%= simple_form_for :query, url: clients_products_path, method: :get, wrapper: :inline_form, html: {class: 'form-inline'} do |f| %>
<%= f.input :keyword, placeholder: "Recherche" %>
<%= f.submit "Valider" %>
<% end %>
In my product_controller.rb
class Clients::ProductsController < ApplicationController
def index
filter_products if params[:query].present?
#products ||= Product.all
end
private
def filter_products
return if params[:query].blank?
#products = Product.where('lower(title) LIKE ?', params[:query][:keyword]) if params[:query][:keyword].present?
end
end
My query seems to be correct as I can find product in the rails console.
but it doesn't display anything in the product#index...
Where am I wrong?
update
All products are well displayed, and everything disapear when I make a query
clients/products/index.html.erb
<% #products.each do |product| %>
<%= link_to clients_product_path(product) do %>
<%= image_tag(product.attachments.first.url) %>
<%= product.title %>
<%= product.price %>
<% end %>
<% end %>
here is the result
http://localhost:3000/clients/products?utf8=%E2%9C%93&query%5Bkeyword%5D=jean&commit=Valider
I believe your issue lies here:
#products = Product.where('lower(title) LIKE ?', params[:query][:keyword])
You need to either prepend, append or wrap your query with %. For example:
#products = Product.where('lower(title) LIKE ?', "%#{params[:query][:keyword]}%")
# Think it's the above, though could be the following:
# #products = Product.where('lower(title) LIKE "%?%"', params[:query][:keyword])
If you have a read on SQL's LIKE operator, the % operates something like a wildcard. Without these, you're searching for an exact match, rather than a phrase contained within the title. Docs are here.
Give that a shot and let me know how you get on.
First of all you are checking params[:query] twice(once when calling filter_products and second time in that function)
And their is something wrong with you filter_products function.
When you do #products ||= Product.all you get blank ActiveRecordRelation if query returns empty relation. In other words #products will always be blank if query[:keyword] doesn't match the title.
Try changing your index function to:
def index
#products = Product.where('lower(title) LIKE %?%', params[:query][:keyword].downcase) if params[:query][:keyword].present?
puts #products
#products ||= Product.all
end
If it still returns blank, then try to print #products variable.

Rails: Display database query on HTML page, ActiveRecord

I am trying to create a search form for my rails app, the user enters a query and based off that query, I want them to see a list of links associated with that query (these links come from my 'links' table). I created the HTML form and it has the user enter a query. I use this query
<%= form_tag links_path, :method => 'get' do %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "search", :name => nil %>
</p>
<% end %>
In my controller for the links table, I have an if statement that checks what the user has entered and it assigns Link.where('title LIKE ?', '%{#params[:search]}%') to #links. and then converts it to an array (.to_a)
Here is the statement in the index action:
def index
#links = Link.all
if params[:search]
##links = Link.find(:all, :conditions => ['title LIKE ?', '%{#params[:search]}%'])
#links = Link.where('title LIKE ?', '%{#params[:search]}%')
#links.to_a
end
end
In my index.html.erb I would like to display the result. I used <%= #links %> however, it displays the ActiveRecord: #<Link::ActiveRecord_Relation:0x0000000d5c69f0>
How could I convert the query result ActiveRecord into an array, so then I could be able to index it?
Thanks.
Don't EVER EVER EVER EVER use
#links = Link.where('title LIKE ?', '%{#params[:search]}%')
this is a security issue. Check http://railscasts.com/episodes/25-sql-injection.
In order to see all likes as an output just simply do
#links = Link.where('title LIKE ?', params[:search]')
and in Views do
<%= #links.to_a %>
That should help :)
You need to assigns #links like this:
#links = #links.to_a
By the way, if you want render link one-by-one using something like #links.each, you do not need to convert #links to array.

Show Ransack search results on different page

I am using Ransack to add a simple search form on my homepage. I would like the results of the search to show on a different page, instead of on the homepage.
The HomeController has an index action with the #search variable set as follows
def index
#search = User.search(params[:q])
#users = #search.result
end
The view contains
<%= search_form_for #search do |f| %>
<fieldset>
<legend>User</legend>
<ul>
<li>
<%= f.label :first_name_or_last_name_cont %>
<%= f.text_field :first_name_or_last_name_cont %>
</li>
<li>
<%= f.label :email_cont %>
<%= f.text_field :email_cont %>
</li>
</ul>
</fieldset>
<fieldset>
<legend>User's Posts</legend>
<ul>
<li>
<%= f.label :posts_title_cont %>
<%= f.text_field :posts_title_cont %>
</li>
</ul>
</fieldset>
<%= f.submit %>
<% end %>
<%= render 'results' %>
How can I set up the controller so that I can use <%= render 'results' %> in a different view for a different action, say a search action? How can I do this so that when I submit the search form I am directed to a new page for the search action which displays the search results?
Great question! To answer your question, you can create a private method with a redirect_to a different page (that has <%=render 'results' %>) IF search params are passed in your HomeController.
class HomeController < ApplicationController
before_action :search
def index
#search = User.search(params[:q])
#users = #search.result
end
private
def search
if params[:q]
search_params = CGI::escapeHTML(params[:q])
redirect_to (url --> see below how to get the url)
end
end
end
However, if you want to start building out your app, you want your search results to display on that dedicated page, no matter where you are at in the app. I am pasting in a full answer from a small rails app. The code is only slightly different (form_tag instead of search_form_for), but I know it works, so hopefully it will help you.
Below, is a nav bar partial that is displayed across the app and then the relevant code for the home page and the ListingController index action. If search params are passed, then index.html.erb renders the #listings partial (_listing.html.erb) and nothing below the <% else %> tag on the home page.
_navigation.html.erb
<%= form_tag search_path, :method => :get do %>
<div class="form-group">
<%= text_field_tag :search, params[:search], class: "form-control", placeholder: "Search" %>
</div>
<%= submit_tag "Submit", :name => nil, :class => "btn btn-primary" %>
<% end %>
index.html.erb
<% if params[:search] %>
<h2>Search Results</h2>
<%= render #listings %>
<% else %>
...what usually shows up on my home page with no search results.
<% end %>
listings_controller
def index
#listings = Listing.search(params[:search])
end
routes.rb
get 'search' => "listings#search"
This works great. However, if I am in a different view/controller, like the one showing all the categories, and try to search, then it basically searches the current page. So, I added the following to the categories controller:
categories_controller
before_action :search
......
private
def search
if params[:search]
search_params = CGI::escapeHTML(params[:search])
redirect_to ("/listings?utf8=%E2%9C%93&search=#{search_params}")
end
end
BUT, for your specific app, to get the search to redirect to the home page and display the search results, first do a search on your home page and see what is generated in the url. Let's say I typed 'cheese' (/listings?utf8=%E2%9C%93&search=cheese). Notice the %E2%9C%93...you may not see this b/c this normally displays as a check in the url on your browser (http://unicode-search.net/unicode-namesearch.pl?term=mark)...so just paste it into text wrangler or stackoverflow text area to get the 'full url' like above. Then at the end of the url, just replace what you typed into the search box with #{search_params}.
This passes whatever was typed into the search box to your dedicated search results page (in my case index.html.erb)!
Here is some documentation on CGI escapeHTML (for security reasons): http://ruby-doc.org/stdlib-2.0/libdoc/cgi/rdoc/CGI.html#method-c-escapeHTML

Resources