In my offer.rb model I am doing some filtering after clients who have these offers.
But I want to be able to pass another param in my scope to search by, for example, the age of a client or so.
This is what I have now in my offer model:
scope :with_client_id, lambda { |client_id| where(:client_id => client_id) }
In the view:
<%= f.select(:with_client_id, options_for_select(current_partner.clients.collect{|c| [c.name, c.id]}), { include_blank: true}) %>
How can I pass a client's age per say in this scope?
Thanks!
Two Options
Using a "splat"
scope :with_client_id_and_age, lambda { |params| where(:client_id => params[0], :age => params[1]) }
And then you'd have to call it with:
Offer.with_client_id_and_age( client_id, age )
Using a params hash
scope :with_client_id_and_age, lambda { |params| where(:client_id => params[:client_id], :age => params[:age]) }
And then you'd have to call it with:
Offer.with_client_id_and_age( { client_id: client_id, age: age } )
I left the scope unchanged and just modified the select in the view:
<%= f.select(:with_client_id, options_for_select(current_partner.clients.collect{|c| [c.name_and_age, c.id]}), { include_blank: true}) %>
With the name_and_age method in the clients controller:
def name_and_age
[name, age].compact.join " "
end
I can type some age too in the select2 box to make the filterring.
Related
I'm trying to implement basic records filtration in my Rails application. But I have problem with filtering by belongs_to and has_many associations.
I have 3 models: Job, JobCategory, Tag. Tags are implemented via acts_as_taggable_on gem.
There are some scopes in my Job model, that looks like work correctly:
class Job < ApplicationRecord
belongs_to :job_category
acts_as_taggable_on :tags
scope :join_tags, -> { includes(:tags) }
scope :salary_min, lambda { |salary_min|
where('salary_min >= ? or salary_min IS NULL', salary_min)
.where('salary_max >= ? or salary_max IS NULL', salary_min)
.where('salary_min >= ? or salary_min IS NULL', salary_min)
}
scope :salary_max, lambda { |salary_max|
where('salary_min <= ? or salary_min IS NULL', salary_max)
.where('salary_max <= ? or salary_max IS NULL', salary_max)
}
scope :by_category, lambda { |*category_id|
includes(:job_categories).where(job_category_id: category_id)
}
scope :tagged_with_id, lambda { |*tag_id|
joins(:taggings).where(taggings: { tag_id: tag_id })
}
end
My index action in JobsController is:
def index
if params[:tag]
#jobs = Job.tagged_with(params[:tag])
.filter(params.slice(:salary_min, :salary_max, :by_category, :tagged_with_id))
else
#jobs = Job.published.filter(params.slice(:salary_min, :salary_max, :by_category, :tagged_with_id))
.join_tags
end
#job_categories = JobCategory.all
end
I can call 2 scopes from view:
= form_for jobs_path, method: 'get' do |f|
= f.label 'Salary from'
= text_field_tag :salary_min, params[:salary_min]
= f.label 'Salary to'
= text_field_tag :salary_max, params[:salary_max]
= f.submit 'Filter', name: nil
How can I call other scopes to filter records by multiple categories and tags?
I need to call by_category and tagged_with_id scopes, but don't understand how can I do it correctly.
In other forms in my app I use next 2 form helpers:
= f.collection_check_boxes :job_category_ids, JobCategory.order('priority DESC'), :id, :title, hide_label: true
= f.collection_select :tag_list, ActsAsTaggableOn::Tag.all.order(:name), :name, :name, {hide_label: true}, html_options = {class: 'js-example-basic-multiple', multiple: 'multiple'}
I would like to use these helpers, but with passing filters parameters to the controller, if it is possible.
first get your category_ids in an array
category_ids = JobCategory.order('priority DESC').pluck(:id)
then passing in arguments to your scope in the format of an array. This are subset conditions
scope :by_category, ->(category_ids) {
where(job_category_id: category_ids)
}
I checked your profile and given your previous experience on ruby on rails, I believe you will not have problems figuring the rest of the code
thanks!
I have User model which has role (String) attribute. I want to filter my Users by this role attribute.
For filtering I'm using filterific gem.
Here is my index action:
def index
#filterrific = initialize_filterrific(
User,
params[:filterrific],
select_options: {
with_role: User.options_for_select
},
) or return
#users = #filterrific.find.page(params[:page])
end
where options_for_select method is defined:
# user.rb
def self.options_for_select
order('LOWER(role)').map { |e| [e.role, e.id] }
end
and in my view:
<%= f.select(
:with_role,
#filterrific.select_options[:with_role],
{ include_blank: '- Any -' }
) %>
and I have a scope in my user.rb:
scope :with_role, lambda { |roles|
where(role: [*roles])
}
I have only five roles, but in my select each role appears many times, so I don't know how to return unique roles in options_for_select method.
Secondly, options_for_select returns id's of user as a value in each option tag, but I want it to return role itself.
You define options_for_select on the User model. So it pulls in every user record in your database and its associated role entry, along with the user id.
Try the following instead of User.options_for_select in the controller:
select_options: {
with_role: ['Role1', 'Role2', 'Role3']
},
I have edited my first code and now it's better and cleaner thanks to #FunTimeFreddie, but the issue it's not yet properly solved. I'll come back with the right answer sooner.
In a search form I need to filter all menuitems:
1. per category
2. per category and search “query”
3. per min price && || max price
… and so on, with all possible combinations
I’ve manage to make a search in all menuitems with a “query”, min_price and max_price --all possible combinations from the search form. I can NOT manage to have the list of results of the chosen category, what am I doing wrong?
This is my model(edited):
class Menuitem < ActiveRecord::Base
belongs_to :menu_category
include Filterable
scope :newest_first, lambda { order('menuitems.created_at DESC') }
scope :last_one, lambda { order('menuitems.created_at ASC').last }
scope :search_keyword, lambda { |query|
where(["title LIKE ? or body LIKE ?", "%#{query}%", "%#{query}%"]) if query != ""
}
scope :menu_category_id, lambda { |menu_category_id|
where( "menu_category_id = ?", menu_category_id ) if menu_category_id != ""
}
scope :min_price, lambda { |price|
where("price > ?", price) if price != ""
}
scope :max_price, lambda { |price|
where("price < ?", price) if price != ""
}
end
This is my controller(edited):
class MenuitemsController < ApplicationController
def index
#menuitems = Menuitem.newest_first.filter(params.slice(:menu_category_id, :search_keyword, :min_price, :max_price))
end
And this is my view:
<%= simple_form_for :menuitem, :method => 'get', :url => {:action => 'index'} do |f| %>
<p>
<%= f.select :menu_category_id, options_for_select(#menucategories.map {|s| [s.title, s.id]}, params[:menu_category_id]), :selected => params[:menu_category_id], :onchange => "this.form.submit();", prompt: "Select category" %>
</p>
<p>
<%= f.input :search_keyword, input_html: { name: 'search_keyword', :value => params[:search_keyword]}, label: 'search recipe title', :required => false %>
<%= f.input :min_price, input_html: { name: 'min_price', :value => params[:min_price]}, label: 'min price:', :required => false %>
<%= f.input :max_price, input_html: { name: 'max_price', :value => params[:max_price]}, label: 'max price:', :required => false %>
<%= f.button :submit, "search" %>
</p>
<% end %>
You can save yourself the trouble of all the IF statements in your controller for all of the combinations by adding an IF statement within the scopes. For example, and this can similarly be applied to the four scopes associated to your form,
# menuitem model
scope :search_keyword, lambda { |query|
where(["name LIKE ?", "%#{query}%"]) if query != ""
}
This will allow to include only a single line in your controller, the line beneath your first IF statement, as this will handle the blank parameters.
There seems to be two issues with the category parameter. The first is it is a nested parameter within params[:menuitem], so in order to access it we need to call params[:menuitem][:menu_category_id]. Not too sure why this is happening to be honest, though I would recommend in this instance using a form_tag as opposed to form_for, given that we are not adding or editing the menuitems table itself.
The second issue is the category parameter is passed as a string, whereas we need it as an integer. We'll need to convert to parameter before applying it.
Now I'm not familiar with the .filters method (is this part of a gem?) but I got this to work the old fashioned way just by concatenating all the scopes together in one line as follows...
# menuitems controller
def index
#menuitems = Menuitem.newest_first.min_price(params[:min_price]).max_price(params[:max_price]).search_keyword(params[:search_keyword]).menu_category_id(params[:menuitem][:menu_category_id].to_i)
end
Note, another way of changing the data type would be to do so in the scope. You could do this as follows
# menuitem model
scope :menu_category_id, lambda { |menu_category_id|
where( "menu_category_id = ?", menu_category_id.to_i ) if menu_category_id != ""
}
<%= collection_select(:catgory, :id, #categories, :id, :title, {}, data: { behavior: 'category_dropdown' }) %>
In the above code I need to pass a parameter to the title method. Is there any way to do this with collection_select?
<%= collection_select(:catgory, :id, #categories, :id, (:title, #program), {}, data: { behavior: 'category_dropdown' }) %>
Edit:
Looking at the internals for collection_select the text_method. It is eventually passed to a .send method which should allow for element.send(:title, #program). However, I think the issue why I still can't pass the param is that collection select is reading (:title, #program) as two params instead of one.
Use select instead:
select "catgory", "id", #categories.map{|c| [c.title(#program), c.id]}, {}, data: { behavior: 'category_dropdown' }
Should be working.
This can be done with collection_select if your model has an existing parameter you can overwrite:
f.collection_select( :your_model_id,
YourModel.all.map{|ym| ym.name = ym.custom_name(your_parameter); ym},
:id, :name,
{:selected => #model_instance.logic},
{:class => 'your class', :other => "..." } )
For instance I do this to conditionally pluralize my model's name attribute
class MyModel < ActiveRecord::Base
DEFAULT_NAME_COUNT = 99
def pluralized_name(n = DEFAULT_NAME_COUNT)
begin
return name.pluralize(n)
rescue
end
name
end
end
I have a few constants which are arrays that I don't want to create databse records for but I don't know where to store the constants without getting errors.
For example
CONTAINER_SIZES = [["20 foot"],["40 foot"]]
Where can I store this so all models and controller have access to this?
I will write my way to you.
class User < ActiveRecord::Base
STATES = {
:active => {:id => 100, :name => "active", :label => "Active User"},
:passive => {:id => 110, :name => "passive", :label => "Passive User"},
:deleted => {:id => 120, :name => "deleted", :label => "Deleted User"}
}
# and methods for calling states of user
def self.find_state(value)
if value.class == Fixnum
Post::STATES.collect { |key, state|
return state if state.inspect.index(value.to_s)
}
elsif value.class == Symbol
Post::STATES[value]
end
end
end
so i can call it like
User.find_state(:active)[:id]
or
User.find_state(#user.state_id)[:label]
Also if i want to load all states to a select box and if i don't want some states in it (like deleted state)
def self.states(arg = nil)
states = Post::STATES
states.delete(:deleted)
states.collect { |key, state|
if arg.nil?
state
else
state[arg]
end
}
end
And i can use it now like
select_tag 'state_id', User.states.collect { |s| [s[:label], s[:id]] }
I put them directly in the model class.
class User < ActiveRecord::Base
USER_STATUS_ACTIVE = "ACT"
USER_TYPES = ["MANAGER","DEVELOPER"]
end