adding search functionality to a paginated page in RoR - ruby-on-rails

Here is the scenario:
I have a table which renders a list of groups (id,title,description,etc) like below which is paginated, it also has a search form:
<div id="navigation" style="background:white;border:1px outset cadetblue;">
<p>
<% form_tag groups_path, :method => 'get' do %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", :name => nil %>
</p>
<% end %>
</p>
</div>
<table>
.....
</table>
<%= paginate #groups %>
In my groups_controller.rb I have:
#groups = Group.search(params[:search])
#groups = Group.order("id").page(params[:page]).per(15)
which doesn't seem to be working. The pagination works but when I click
the search button it doesn't do anything.
The search function is implemented in the group.rb model:
def self.search(search)
if search
find(:all, :conditions => ['description LIKE ?', "%#{search}%"])
else
find(:all)
end
end
How do I solve this, how do I make my search form work and return results in a paginated way!

Here is the ultimate answer to this post. All the original poster needed to do was this:
#groups = Group.where("description LIKE '%#{params[:search]}%'").order('id').page(params[:page]).per(15)
In an offline discussion, it wasn't working. turns out he had BOTH will_paginate Gem and kaminari Gem for his pagination. He was crapping out when it came to .per(15). Deleting the will_paginate gem fixed it!
will_paginate isn't being actively upgraded, so if you have will_paginate and you are on Rails 3.X, get rid of will_paginate and use kaminari instead, it's a fairly painless swap out, just a bit of css work to format the pagination.

You need to combine those two calls into one, right now you are overriding the first setting of #groups with the second.
I'd add a scope to your group model
class Group
scope :search, lambda { |val| , find(:all, :conditions=>['description LIKE ?', "%#{val}%"]) }
end
Then you can do
#groups = Group.search(params[:search]).order("id").page(params[:page]).per(15)

Related

Search bar + select tag rails

I have a search bar + a dropdown list to search for restaurants or meals and be able to filter by category (French food, Indian, foot, etc.)
index.html.erb
<%= form_tag meals_path, method: 'get', id: 'products_search' do %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= select_tag :search_category, options_for_select(#categories, #categories[0]) %>
<%= submit_tag "Rechercher", name: nil %>
</p>
<% end %>
meals_controller.rb
#meals_search = #meals.search(params[:search], params[:search_category])
model/meal.rb
def self.search(search, search_category)
if search
if search && search_category == "All"
where('name LIKE ?', "%#{search}%")
elsif search == "" && search_category != "All"
where('category = ?', "%#{search_category}%")
else
where('name LIKE ? AND category = ?', "%#{search}%", "%#{search_category}%")
end
else
all
end
end
the search bar works perfectly but the category filter (search_category) does not. I actually get an empty list although you can only filter with category that has at least one restaurant or meal (so the list can't be empty).
Any idea ?
just use ransack, for advanced searching and sorting
OK so, following #vishnuprakash 's suggestion, I used the ransack gem to solve my issue:
I added gem 'ransack' to my Gemfile and ran bundle.
Then I created this form in my index.html.erb:
<%= search_form_for #q do |f| %>
<%= f.search_field :name_cont %>
<%= f.select :category_eq, options_for_select(#categories), include_blank: true %>
<%= f.submit %>
<% end %>
and meals_controller.rb:
#q = #meals.ransack(params[:q])
#meals_search = #q.result(distinct: true)
I also got rid of the function previously created in model/meals.rb.
It now works perfectly ! Do not hesitate to have a look at the ransack dock if you have similar issue. You can also DM me, I'll be happy to help.
Try to change
where('name LIKE ? AND category = ?', "%#{search}%", "%#{search_category}%")
to
where('name LIKE ? AND category = ?', "%#{search}%", search_category)
and
where('category = ?', "%#{search_category}%")
to
where('category = ?', search_category)
The %...% syntax only works with LIKE, not with =.

How to display two differents models in my view with will_paginate in rails?

Hi everyone I have a question , I looking for on google but I don't find an answer.
I'm actually want to display Post model and User model by search in same page and for this I want to use Will_paginate to display all of them .
I try this solution =>
**require 'will_paginate/array'
#posts =Post.search(params[:q]).order("created_at DESC")
#users=User.search(params[:q]).order("created_at DESC")
#all_records = (#posts + #users).paginate(:page =>params[:page], :per_page => 10)**
And In my view I want to display all query found ,so I try something like this =>
**<%unless #posts.nil?%>
<% #posts.each do |post| %>
....
**
**<% unless #users.nil?%>
<% #users.each do |user| %>
...**
<%= will_paginate #all_records %>
But it doesn't work nothing happend, so I want to know how to get #posts and #users records in #all_records to do something like this =>
**
<% #all_records[#posts].each do.. %>
<% #all_records[#usets].each do ..%>
**
I saw some solutions that suggest to use two differentes paginate in my view like =>
**<%= will_paginate #posts %>
<%= will_paginate #users %>**
**But It's not want I want , I would'like only one paginate for all, Please Help me cause I'm going crazy, Thank you **

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.

How do you order by a custom model method that has no attribute in SQL?

Previously I ordered my posts as this:
#posts = Post.find(:all, :order => "created_at DESC")
But now I want to replace created_at with a custom method I wrote in the Post model that gives a number as its result.
My guess:
#posts = Post.find(:all, :order => "custom_method DESC")
which fails..
It fails because you are asking your db to do the sorting.
#posts = Post.all.sort {|a,b| a.custom_method <=> b.custom_method}
Note that this becomes non-trivial when you want to start paging results and no longer wish to fetch .all. Think about your design a bit before you go with this.
Just to expand on #Robbie's answer
Post.all.sort_by {|post| post.custom_method }.reverse
As the first answer noted, order is an Active Record command that essentially does a SQL query on your database, but that field doesn't actually exist in your database.
As someone else commented, you can more cleanly run the Ruby method sort_by by using the ampersand (more info here):
Post.all.sort_by(&:custom_method)
However, things do get complicated depending on what you want to do in your view. I'll share a case I recently did in case that helps you think through your problem. I needed to group my resource by another resource called "categories", and then sort the original resource by "netvotes" which was a custom model method, then order by name. I did it by:
Ordering by name in the controller: #resources = Resource.order(:name)
Grouping by category in the outer loop of the view: <% #resources.group_by(&:category).each do |category, resources| %>
Then sorting the resources by votes in the partial for resources: <%= render resources.sort_by(&:netvotes).reverse %>
The view is a bit confusing, so here is the full view loop in index.html.erb:
<% #resources.group_by(&:category).each do |category, resources| %>
<div class="well">
<h3 class="brand-text"><%= category.name %></h3>
<%= render resources.sort_by(&:netvotes).reverse %>
</div>
<% end %>
And here is the _resource.html.erb partial:
<div class="row resource">
<div class="col-sm-2 text-center">
<div class="vote-box">
<%= link_to fa_icon('chevron-up lg'), upvote_resource_path(resource), method: :put %><br>
<%= resource.netvotes %><br>
<%= link_to fa_icon('chevron-down lg'), downvote_resource_path(resource), method: :put %>
</div>
</div>
<div class="col-sm-10">
<%= link_to resource.name, resource.link, target: "_blank" %>
<p><%= resource.notes %></p>
</div>
</div>
This is a bit more complicated than what I like but this I like to keep my sort to stay as a active record model so its bit more complicated than just
Post.all.sort_by {|post| post.custom_method }
what I do is:
ids = Post.all.sort_by {|post| post.custom_method }.map(&:ids)
Post.for_ids_with_order(ids)
this is a custom scope in the Post model
#app/models/post.rb
class Post < ApplicationRecord
...
scope :for_ids_with_order, ->(ids) {
order = sanitize_sql_array(
["position(id::text in ?)", ids.join(',')]
)
where(:id => ids).order(order)
}
...
end
I hope that this help
Well, just Post.find(:all) would return an array of AR objects. So you could use Array.sort_by and pass it a block, and since those records are already fetched, you can access the virtual attribute inside the block that sort_by takes.
RDoc: Enumerable.sort_by
Keep in mind that sort_by will return an Array, not an ActiveRecord::Relation, which you might need for pagination or some other some view logic. To get an ActiveRecord::Relation back, use something like this:
order_by_clause = Post.sanitize_sql_array(<<custom method expressed in SQL>>, <<parameters>>)
Post.all.order(Arel.sql(order_by_clause))
in rails 3 we can do this as: Post.order("custom_method DESC")
When upgrading app from rails2 to rails3

Ruby/Rails Simple search form and white spaces

I've implemented a "simple search form" (railscasts #37) in my app, but I wonder how:
1.) I cannot display any results if the keywords field is empty, or if the index page is loaded (I've tried search != "". Maybe there is a better way)
2.) I can add a function which avoid multiple white spaces in the search. (if users wrote something like "benoit+++" or "++benoit" in the search box, it should display results)
I don't want to use thinking sphinx as I would deploy on heroku
Here is the code:
In my user model (user.rb)
def self.search(search)
if search and search != ""
find(:all, :conditions => ['name LIKE ?', "%#{search}%"])
end
In my view (views/users/index.html.erb)
<% form_tag users_path, :method => 'get' do %>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", :name => nil %>
<% end %>
<% if #users and not #users.empty? %>
<% #users.each do |user| %>
<p><%= link_to "#{user.name}", user %></p>
.
.
.
<% end %>
<% end %>
and in my controller ( users_controller.rb)
def index
#users = User.search(params[:search])
end
Thanks for any help or ressources!!
I would change the search method:
def self.search(search)
search.blank? ? [] : all(:conditions => ['name LIKE ?', "%#{search.strip}%"])
end
something.blank? returns true if something is nil or blank.
something.strip removes all the spaces at the beginning or end of a string. If name could be made of more than 1 word and you wanted to prevent from having more than one space between the words, you could do search.strip.squeeze(" ")
Oh, I don't think you need the if #users && !#users.empty? clause in your view now.

Resources