I have an app where the user can create a new project and search for previous projects. On the “create new project” page, the user has the option to enter new info for each field, or select from dropdown menus, if the data is already in the database. As for the search function on the search page. The user can search on many different fields, multiple ways.
UPDATE:
Following a suggestion below, I have gone ahead a built a table that holds the many technologies per project.
Search action
def search
@search = params[:client], params[:industry], params[:role], params[:tech_id], params[:business_div], params[:project_owner], params[:status], params[:start_date_dd], params[:start_date_A], params[:start_date_B], params[:keywords]
@project_search = Project.search(*@search).order(sort_column + ' ' + sort_direction).paginated_for_index(per_page, page)
@search_performed = !@search.reject! { |c| c.blank? }.empty?
@project = Project.new(params[:project])
@all_technols = Technol.all
@technol = Technol.new(params[:tech])
params[:technols][:id].each do |technol|
if !technol.empty?
@project_technol = @project.projecttechnols.build(:technol_id => technol)
end
end
respond_to do |format|
format.html # search.html.erb
format.json { render :json => @project }
end
end
New action:
def new
@project = Project.new
@all_technols = Technol.all
@project_technol = @project.projecttechnols.build
@project_technol = Projecttechnol.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: @project }
end
end
Create action
def create
@all_technols = Technol.all
@project = Project.new(params[:project])
@technol = Technol.new(params[:tech])
params[:technols][:id].each do |technol|
if !technol.empty?
@project_technol = @project.projecttechnols.build(:technol_id => technol)
end
end
@project.client = params[:new_client] unless params[:new_client].blank?
@project.project_owner = params[:new_project_owner] unless params[:new_project_owner].blank?
@project.tech = params[:new_tech] unless params[:new_tech].blank?
@project.role = params[:new_role] unless params[:new_role].blank?
@project.industry = params[:new_industry] unless params[:new_industry].blank?
@project.business_div = params[:new_business_div] unless params[:new_business_div].blank?
respond_to do |format|
if @project.save
format.html { redirect_to @project, notice: 'Project was successfully created.' }
format.json { render json: @project, status: :created, location: @project }
else
format.html { render action: "new" }
format.json { render json: @project.errors, status: :unprocessable_entity }
end
end
end
Tech part of the new view:
<%= stylesheet_link_tag "new" %>
<h1>Create New Project</h1>
<div class="tech">
<%#= label_tag :new_tech, "Technologies Used :" %><br/>
<%= text_field_tag :new_tech, nil, :maxlength => 30 %>
Or
<%#= f.select( :tech, Project.all.map {|p| [p.tech]}.uniq, { :prompt => "Select a previous Technology"}, { :multiple => true, :size => 5 }) %>
<!/div>
<% common_techs = [['Mainframe'],['UNIX'],['Windows Servers'],['Networking'],['CISCO'], ['Win7'], ['Telephony'], ['Web services'], ['Website'], ['Cloud'], ['Virtualisation'], ['Data Centre']] %>
<% db_techs = Technol.all.map {|p| [p.tech]}.uniq %>
<% all_tech = common_techs + db_techs %>
<%= fields_for(@project_technol) do |ab| %>
<%= ab.label "Select previous technologies" %><br/>
<%= collection_select(:technols, :id, @all_technols, :id, :tech, {}, {:multiple => true} ) %>
</div>
<% end %>
<%= stylesheet_link_tag "new" %>
<h1>Create New Project</h1>
<div class="tech">
<%#= label_tag :new_tech, "Technologies Used :" %><br/>
<%= text_field_tag :new_tech, nil, :maxlength => 30 %>
Or
<%#= f.select( :tech, Project.all.map {|p| [p.tech]}.uniq, { :prompt => "Select a previous Technology"}, { :multiple => true, :size => 5 }) %>
<!/div>
<% common_techs = [['Mainframe'],['UNIX'],['Windows Servers'],['Networking'],['CISCO'], ['Win7'], ['Telephony'], ['Web services'], ['Website'], ['Cloud'], ['Virtualisation'], ['Data Centre']] %>
<% db_techs = Technol.all.map {|p| [p.tech]}.uniq %>
<% all_tech = common_techs + db_techs %>
<%= fields_for(@project_technol) do |ab| %>
<%= ab.label "Select previous technologies" %><br/>
<%= collection_select(:technols, :id, @all_technols, :id, :tech, {}, {:multiple => true} ) %>
</div>
<% end %>
</div> <%#= small div %>
<% end %>
<div class="back_button2">
<%= button_to "Back", projects_path , :class => "button", :method => "get" %>
</div>
</div> <%#= small div %>
<% end %>
<div class="back_button2">
<%= button_to "Back", projects_path , :class => "button", :method => "get" %>
</div>
Search Page:
<html>
<%= stylesheet_link_tag "search" %>
<body>
<div id ="title1">Exception Database Search</div>
<div id = "search_op">
<%= form_tag search_path, method: :get do %>
<div class="client">
Client :
<%= select(@projects, :client, Project.order("client").map{|p| [p.client]}.uniq, :prompt => "-Any-", :selected => params[:client]) %></br>
</div>
<div class="industry">
Industry :
<%= select(@projects, :industry, Project.order("industry").map {|p| [p.industry]}.uniq, :prompt => "-Any-", :selected => params[:industry]) %></br>
</div>
<div class="role">
Role :
<%= select(@projects, :role, Project.order("role").map {|p| [p.role]}.uniq, :prompt => "-Any-", :selected => params[:role]) %></br>
</div>
<div class="tech">
<% common_techs = [['Mainframe'],['UNIX'],['Windows Servers'],['Networking'],['CISCO'], ['Win7'], ['Telephony'], ['Web services'], ['Website'], ['Cloud'], ['Virtualisation'], ['Data Centre']] %>
<% db_techs = Project.order("tech").map {|p| [p.tech]}.uniq %>
<% all_tech = Project.order("tech").map {|p| [p.tech]} + Project.order("tech").map {|p| [p.tech2]} + Project.order("tech").map {|p| [p.tech3]} + Project.order("tech").map {|p| [p.tech4]} + Project.order("tech").map {|p| [p.tech5]} %>
<%= fields_for(@project_technol) do |ab| %>
<%= collection_select(:technols, :id, @all_technols, :id, :tech, {}, {:multiple => true } ) %>
</div>
<% end %>
<div class="business_div">
Business Division :
<%= select(@projects, :business_div, Project.order("business_div").map {|p| [p.business_div]}.uniq, :prompt => "-Any-", :selected => params[:business_div]) %></br>
</div>
<div class="project_owner">
Project Owner :
<%= select(@projects, :project_owner, Project.order("project_owner").map {|p| [p.project_owner]}.uniq, :prompt => "-Any-", :selected => params[:project_owner]) %></br>
</div>
<div class="date1">
Select start dates from :
<%= select_tag "start_date_dd", options_for_select({
"Select a period" => "",
"3 days ago" => DateTime.now.to_date - 3.days, # = 259_200 sec.
"1 week ago" => DateTime.now.to_date - 1.week, # = 604_800 sec.
"1 month ago" => DateTime.now.to_date - 1.month, # = 2_592_000 sec.
"6 months ago" => DateTime.now.to_date - 6.months, # = 15_552_000 sec.
"1 year ago" => DateTime.now.to_date - 1.year, # = 31_557_600 sec.
}, :selected=>params[:start_date_dd] )%>
</div>
<%#= until now <%= l DateTime.now.to_date %><%#=,%>
<h4> OR</h4>
<div class="date2">
exact start dates
<%= text_field_tag :start_date_A, params[:start_date_A], :style => 'width: 80px;' %>
-
<%= text_field_tag :start_date_B, params[:start_date_B], :style => 'width: 80px;' %></br>
</div>
<div class="status">
Status :
<%= select(@projects, :status, Project.order("status").map {|p| [p.status]}.uniq, :prompt => "-Any-", :selected => params[:status]) %></br>
</div>
<div class="keywords">
Keywords :
<%= text_field_tag :keywords, params[:keywords] %></br>
</div>
<div class="results">
Results per page: <%= select_tag :per_page, options_for_select([10,20,50], :selected=>params[:per_page]), { :onchange => "this.form.submit();"} %></br>
<div class="buttons">
<div id="search_button">
<%= submit_tag "Search", name: nil, :class => "button" %>
</div>
<% end %>
<div class="home_button">
<%= button_to "Home", projects_path, :class => "button", :method => "get" %>
</div>
<div id="reset_button">
<%= button_to "Reset Search", search_path, :class => "button", :method => "get" %>
</div>
</div>
</div> <%#search_op div%>
<div class ="menu"></div>
<div id ="section3">
<% if @project_search.total_entries > 0 %>
<% if @search_performed %>
<% if @search_performed %>
<%= hidden_field_tag :direction, params[:direction] %>
<%= hidden_field_tag :sort, params[:sort] %>
<%= hidden_field_tag :per_page, params[:per_page] %>
<%= hidden_field_tag :page, params[:page] %>
<%= will_paginate (@project_search), :class => 'will' %>
<% end %>
</body>
</html>
Models:
class Projecttechnol < ActiveRecord::Base
attr_accessible :project_id, :technol_id
belongs_to :technol
belongs_to :project
end
class Technol < ActiveRecord::Base
attr_accessible :tech
has_many :projecttechnols
has_many :projects, :through => :projecttechnols
end
class Project < ActiveRecord::Base
attr_accessible :edited_first_name, :edited_last_name, :first_name, :last_name, :business_div, :client, :customer_benifits, :edited_date, :end_date, :entry_date, :financials, :industry, :keywords, :lessons_learned, :project_name, :project_owner, :role, :start_date, :status, :summary, :tech_id
validates_presence_of :business_div, :client, :customer_benifits, :end_date, :financials, :industry, :keywords, :lessons_learned, :project_name, :project_owner, :role, :start_date, :status, :summary#, :tech
validates_format_of :industry, :with => /\A[a-zA-Z]+\z/, :message => "field should only have letters"
validates_format_of :business_div, :with => /\A[a-zA-Z]+\z/, :message => "field should only have letters"
validates_format_of :client, :with => /\A[a-zA-Z]+\z/, :message => "field should only have letters"
validates_format_of :project_owner, :with => /\A[a-zA-Z]+\z/, :message => "field should only have letters"
validates_format_of :role, :with => /\A[a-zA-Z]+\z/, :message => "field should only have letters"
has_many :projecttechnols
has_many :technols, :through => :projecttechnols
def self.like(text); "%#{text}%"; end
def self.search(search_client, search_industry, search_role, search_tech_id, search_business_div, search_project_owner, search_exception_pm, search_status, search_start_date_dd, search_start_date_A, search_start_date_B, search_keywords)
# start with a scoped query, to apply more scopes on it afterwards
_projects = Project.scoped
# then, for each of the parameters, apply the scope only if present
if search_client.present?
_projects = _projects.where ['client LIKE ?', like(search_client)]
end
if search_industry.present?
_projects = _projects.where ['industry LIKE ?', like(search_industry)]
end
if search_role.present?
_projects = _projects.where ['role LIKE ?', like(search_role)]
end
if search_tech_id.present?
_projects = _projects.joins(:technols).where("tech.id" => search_tech_id)
end
if search_business_div.present?
_projects = _projects.where ['business_div LIKE ?', like(search_business_div)]
end
if search_project_owner.present?
_projects = _projects.where ['project_owner LIKE ?', like(search_project_owner)]
end
if search_status.present?
_projects = _projects.where ['status LIKE ?', like(search_status)]
end
todays_date = DateTime.now.to_date
if !search_start_date_A.blank? or !search_start_date_B.blank?
search_start_date_A = Date.parse(search_start_date_A).strftime("%Y-%m-%d")
search_start_date_B = Date.parse(search_start_date_B).strftime("%Y-%m-%d")
todays_date = nil
search_start_date_dd = nil
end
if search_start_date_dd.blank?
todays_date = nil
end
if search_start_date_A.present? or search_start_date_B.present?
_projects = _projects.where [' DATE(start_date) BETWEEN ? AND ?', search_start_date_A, search_start_date_B]
end
if search_start_date_dd.present?
_projects = _projects.where ['DATE(start_date) BETWEEN ? AND ?', search_start_date_dd, todays_date]
end
if search_keywords.present?
_projects = _projects.where ['keywords LIKE ?', like(search_keywords)]
end
# now you have applied only the present scopes. return the result, and watch
# the query as it executes.
_projects
end
def self.paginated_for_index(projects_per_page, current_page)
paginate(:per_page => projects_per_page, :page => current_page)
end
end
I can’t seem to get the new technologys on the “new project page” to save in the Technols table. I also can’t get the search to work properly. Searching for anything in the technology field returns nothing.
Ok, third question of yours about the same project, it starts to be clear where you want to go.
Have you considered storing technologies in a separate table, and an additional join table for the relationship to the Product table?
This way you can select any number of techs for a project, you can search using only one field (it will get only projects related to the chosen technology) and you won’t have such complex code.
Use a select tag to choose the technologies in the project form. Validate the project object if you want it to have at least one technology or similar constraint. Use a select on the whole “Technology” table to do the search box. And use
instead of all that stuff concatenating LIKEs
EDIT: a way to search for projects related to many techs.
To allow searching for “many techs” you can use a multiselect field. You can also add an option for “AND”-ing or “OR”-ing the conditions (that is, search for porjocets related to all the selected techs or any of the selected techs.
In the controller, when you get the list of tech IDs, to search for projects with ALL the selected techs you have to use some SQL magic:
On the other side, to search for ANY tech among the selected ones, you can do