I have a controller method for searching and listing invoices, sales orders and homes based on params name, how to simplify this index method`
def index
#so = QbwcSalesOrder.paginate(:page => params[:page], :per_page => 30)
if params[:qs] == "Sales Orders"
#so = #so.where("ref_number = ? ",params[:keyword]) if params[:keyword].present?
elsif params[:qs] == "Invoices"
#so = QbwcInvoice.paginate(:page => params[:page], :per_page => 30)
#so = #so.where("ref_number = ? ",params[:keyword]) if params[:keyword].present?
elsif params[:qs] == "All Homes"
#so = QbwcHome.paginate(:page => params[:page], :per_page => 30)
elsif params[:qs] == "Existing Homes"
#so = QbwcHome.where(record_type: "FromQB").paginate(:page => params[:page], :per_page => 30)
end
end
First of all unify hash syntax and indentation, it's hard to read.
Second paginate once at the end of the index method, looks like you paginate always the same way.
Third think about scopes or query object https://mkdev.me/en/posts/how-to-use-query-objects-to-refactor-rails-sql-queries
Here is simplified version, you can use it in index or move to the query object:
def index
#so = QbwcSalesOrder.all
#so = #so.where("ref_number = ? ", params[:keyword]) if params[:keyword] && params[:qs] == "Sales Orders"
#so = #so.where("ref_number = ? ", params[:keyword]) if params[:keyword] && params[:qs] == "Invoices"
#so = #so.where(record_type: "FromQB") if params[:qs] == "Existing Homes"
#so = #so if params[:qs] == "All Homes"
#so = #so.paginate(page: params[:page], per_page: 30)
end
To me this looks like the method is doing way to much and needs to be split into managable parts:
def index
#so = qs_model_scope(params[:qs])
.paginate(page: params[:page], per_page: 30)
.then do |scope|
filter_by_keyword(scope)
end
end
# ...
private
def qs_model_scope(qs)
case qs
when "Invoices":
QbwcInvoice.all
when "All Homes":
# Smelly - should be a model scope
QbwcHome.where(record_type: "FromQB")
else
QbwcSalesOrder.all
end
end
def filter_by_keyword(scope)
if params[:keyword].present? && ["QbwcSalesOrder","QbwcInvoice"].include?(scope.model.name)
# no need to use a SQL string
scope.where(ref_number: params[:keyword])
else
scope
end
end
If the complexity of this continues to grow you should consider moving this filtering process out of the controller and into a separate object (such as a form object or service object or just a PORO) so that it can be tested in isolation.
I'd split the logic into separate methods. As already mentioned in another answer it makes sense to move it to a query object or something.
def index
scope = invoices || all_homes || existing_homes || sales_orders
#so = scope.paginate(:page => params[:page], :per_page => 30)
end
private
def invoices
return if params[:qs] != 'Invoices'
return QbwcInvoice if params[:keyword].blank?
QbwcInvoice.where(ref_number: params[:keyword])
end
def all_homes
QbwcHome if params[:qs] == 'All Homes'
end
def existing_homes
QbwcHome.where(record_type: 'FromQB') if params[:qs] == 'Existing Homes'
end
def sales_orders
if params[:qs] == 'Sales Orders' && params[:keyword].present?
QbwcSalesOrder.where(ref_number: params[:keyword])
else
QbwcSalesOrder
end
end
Related
Alrighty, so I have searched on here repeatedly on this as well as other places but have yet to find the solution that relates to me. Probably because the previous questions were some time ago.
My problem is that I had first added pagination and then I was required to add a search so that users could search for products. When they do the search it's just supposed to open the products page. If I took out the search, the pagination doesn't give me an error.
The error I get now is
''undefined method `total_pages' for # Product::ActiveRecord_Relation:''
and the line of code highlighted for the error is the pagination in the index.html.erb.
What am I missing? Thanks for any guidance, this newbie needs it!
This is the products_controller:
def index
if Rails.env == "development"
name_env = "name LIKE ?"
else
name_env = "name ilike ?"
end
if params[:q]
search_term = params[:q]
#products = Product.search(search_term)
else
#products = Product.all
#products = Product.paginate(:page => params[:page], per_page: 4)
end
end
This is the index.html.erb :
<div class="pagination">
<%= will_paginate #products %>
</div>
You have missed paginate method when the search, it #products = Product.search(search_term) will be like this
.....
if params[:q]
search_term = params[:q]
#products = Product.search(search_term).paginate(:page => params[:page], per_page: 4)
else
#products = Product.all.paginate(:page => params[:page], per_page: 4)
.....
Additionally Remove this #products = Product.all don't need this.
After all, you just paste this instead of your code, it reduced
def index
if Rails.env == "development"
name_env = "name LIKE ?"
else
name_env = "name ilike ?"
end
if params[:q]
search_term = params[:q]
#products = Product.search(search_term)
else
#products = Product.all
end
#products = #products.paginate(:page => params[:page], per_page: 4)
end
Modify index action as follows:
def index
if Rails.env == "development"
name_env = "name LIKE ?"
else
name_env = "name ilike ?"
end
#products = params[:q] ? Product.search(params[:q]) : Product.scoped
#products.paginate(:page => params[:page], per_page: 4)
end
you should use paginate for searching too.
I am using sunspot 2.2.2 in my rails app for searching results,
I have this code for grouping in my model:
def self.search_products(params, currency = nil, page_uri = nil)
search_products = Sunspot.search(VariantPrice) do
group :code do
if params[:gallery_order].present?
order_by :price, params[:gallery_order].downcase.to_sym
elsif params[:new_arrival].present? || params[:name] == "new-arrivals"
order_by :product_created_at, :desc
else
if params[:fashion_type] == "fashion"
order_by :price, :asc
elsif params[:sort] != "lowhigh"
order_by :price, :asc
else
order_by :price, :asc
end
end
limit 1
end
end
and I have this code in my controller :
variant_prices = Product.search_products(params, #currency, request.original_fullpath)
#variant_prices = []
variant_prices.group(:code).groups.each do |group|
group.results.each do |result|
#variant_prices << result
end
end
#variant_prices = #variant_prices.paginate(:page => params[:page], :per_page => PER_PAGE_VALUE)
#variant_prices_count = variant_prices.group(:code).total
now I am getting the expected count that is #variant_prices_count, which is 1400 in my case, but I am getting #variant_prices count as 60 which is wrong in my case , here I was expecting to get 1400.and then I want to paginate with this result. cant understand whether it's will_paginate issue or something else.
Help!
You can get 1400 from the paginate instance also by Total entries
by this way replace count with total_entries
#variant_prices = #variant_prices.paginate(:page => params[:page], :per_page => PER_PAGE_VALUE)
#variant_prices.total_entries#it will return toal entries 1400
I used will_paginate to paginate, now I want to control how many records in one page.
I do know how to interact data from the select_tag. please tell me how to return the data to
#page in controller.
I used
<td><%= select_tag "count", "<option>10</option> <option>20</option>".html_safe%></td>
Controller
def index
#page = 10;
#users = User.order(:username).joins(:biography).where("`is_active?` = true AND `last_sign_in_at` > DATE_SUB(NOW(), INTERVAL 6 MONTH) ").paginate(:page => params[:page], :per_page => #page)
end
You can use the params[:count] like this to get the number of records per page based on the value selected in the select_tag
def index
#page = params[:count] #here
#users = User.order(:username).joins(:biography).where("is_active? = true AND last_sign_in_at > DATE_SUB(NOW(), INTERVAL 6 MONTH) ").paginate(:page => params[:page], :per_page => #page)
end
i am trying to delete from list but when i am trying this it is getting deleted from database
#course = Course.find(params[:id])
#search = Lesson.search(params[:q])
#lessons = #search.result.paginate(:page => params[:page], :per_page => 10)
#search.build_condition if #search.conditions.empty?
#course.lessons.each do |lesson|
#lessons.each do |l|
if lesson.id == l.id
#lessons.delete(l)
end
end
end
I am getting this error: delete_all doesn't support limit scope
Thanking you
Delete is an ActiveRecord method. I assume you don't want to delete it from the database but from the result list. You can do it like this:
#course.lessons.each do |lesson|
#lesson.reject { |l| l.id == lesson.id }
end
I have this terribly large controller in my app. I'd really like to make it as skinny as possible. Below is some of the code, showing the types of things I'm currently doing.. I'm wondering what things I can move out of this?
A note - this is not my exact code, a lot of it is similar. Essentially every instance variable is used in the views - which is why I dont understand how to put the logic in the models? Can models return the values for instance variables?
def mine
#For Pusher
#push_ch = "#{current_user.company.id}"+"#{current_user.id}"+"#{current_user.profile.id}"
#Creating a limit for how many items to show on the page
#limit = 10
if params[:limit].to_i >= 10
#limit = #limit + params[:limit].to_i
end
#Setting page location
#ploc="mine"
#yourTeam = User.where(:company_id => current_user.company.id)
#Set the user from the param
if params[:user]
#selectedUser = #yourTeam.find_by_id(params[:user])
end
#Get all of the user tags
#tags = Tag.where(:user_id => current_user.id)
#Load the user's views
#views = View.where(:user_id => current_user.id)
if !params[:inbox]
#Hitting the DB just once for all the posts
#main_posts = Post.where(:company_id => current_user.company.id).includes(:status).includes(:views)
#main_posts.group_by(&:status).each do |status, posts|
if status.id == #status.id
if #posts_count == nil
#posts_count = posts
else
#posts_count = #posts_count + posts
end
elsif status.id == #status_act.id
if #posts_count == nil
#posts_count = posts
else
#posts_count = #posts_count + posts
end
end
end
if params[:status] == "All" || params[:status] == nil
#posts = Post.search(params[:search]).status_filter(params[:status]).user_filter(params[:user]).order(sort_column + " " + sort_direction).where(:company_id => current_user.company.id, :status_id => [#status.id, #status_act.id, #status_def.id, #status_dep.id, #status_up.id]).limit(#limit).includes(:views)
else
#posts = Post.search(params[:search]).status_filter(params[:status]).user_filter(params[:user]).order(sort_column + " " + sort_direction).where(:company_id => current_user.company.id).limit(#limit).includes(:views)
end
elsif params[:inbox] == "sent"
#yourcompanylist = User.where(:company_id => current_user.company.id).select(:id).map(&:id)
#yourcompany = []
#yourcompanylist.each do |user|
if user != current_user.id
#yourcompany=#yourcompany.concat([user])
end
end
if params[:t]=="all"
#posts = Post.search(params[:search]).status_filter(params[:status]).user_filter(params[:user]).tag_filter(params[:tag], current_user).order(sort_column + " " + sort_direction).where(:user_id => current_user.id).includes(:views, :tags).limit(#limit)
elsif params[:status]!="complete"
#posts = Post.search(params[:search]).status_filter(params[:status]).user_filter(params[:user]).tag_filter(params[:tag], current_user).order(sort_column + " " + sort_direction).where(:user_id => current_user.id).includes(:views, :tags).limit(#limit)
elsif params[:status]!=nil
#posts = Post.search(params[:search]).status_filter(params[:status]).user_filter(params[:user]).tag_filter(params[:tag], current_user).order(sort_column + " " + sort_direction).where(:user_id => current_user.id).includes(:views, :tags).limit(#limit)
end
end
respond_to do |format|
format.html # index.html.erb
format.js # index.html.erb
format.xml { render :xml => #posts }
end
end
You can start by moving logic into the model...
A line like this screams of feature envy:
#push_ch = "#{current_user.company.id}"+"#{current_user.id}"+"#{current_user.profile.id}"
I would recommend moving it into the model:
#user.rb
def to_pusher_identity
"#{self.company_id}#{self.id}#{self.profile_id}"
end
And then in your controller
#push_ch = current_user.to_pusher_identity
At this point you could even move this into a before_filter.
before_filter :supports_pusher, :only => :mine
Another thing you can do is create richer associations, so you can express:
#tags = Tag.where(:user_id => current_user.id)
as
#tags = current_user.tags
Another example would be for main posts, instead of
Post.where(:company_id => current_user.company.id).includes(:status).includes(:views)
you would go through the associations:
current_user.company.posts.includes(:status).includes(:views)
When I'm drying out a controller/action I try to identify what code could be (should be?) offloaded into the model or even a new module. I don't know enough about your application to really point to where these opportunities might lie, but that's where I'd start.
Few quick ideas:
Consider using respond_to/respond_with. This controller action can be splitted up to two separate ones - one for displaying #main_posts, another for params[:inbox] == "sent". The duplicate code can be removed using before_filters.
Also, a couple of gem suggestions:
use kaminari or will_paginate for pagination
meta_search for search and sorting