Rails 4 searching multiple models in a separate controller
I am trying to implement a complex model search in a separate controller. I have a student model. The whole app has a front page handled by a separate main_controller that doesn't have a model. Master_controller and its associated index pointer should provide the first page and display data from multiple models.
Now I want to search for a model with multiple search criteria of different types. Search criteria: string comparison, number comparison, and boolean (e.g. active if true, only active students show all students). Railscast # 111 showed how to create such a search based on a model and a separate search controller. I created a controller like this and it works great. I am stuck showing the relevant parts in my main / index.
Here is the code:
Main / index.html.haml
- model_class = Adult
- model_class = Pupil
- model_class = MainSearch
.page-header
= render :partial => 'main_searches/form', :main_search => MainSearch.new
Only the form call is here at the moment.
models / main_search.rb
class MainSearch < ActiveRecord::Base
def pupils
@pupils ||= find_pupils
end
private
def find_pupils
pupils = Pupil.order(:name_sur)
pupils = pupils.where(id: id_search) if id_search.present?
pupils = pupils.where("name_first like ?", "%#{name_first}%") if name_first.present?
pupils = pupils.where("name_sur like ?", "%#{name_sur}%") if name_sur.present?
pupils = pupils.where(active: "true") if active == true #show only active or all
pupils
end
end
This defines the search.
Controllers / main_searches_controller.rb
class MainSearchesController < ApplicationController
before_action :set_main_search, only: [:show, :update, :destroy]
def show
@main_search = MainSearch.find(params[:id])
end
def new
@main_search = MainSearch.new
end
def create
@main_search = MainSearch.new(main_search_params)
if @main_search.save
redirect_to root_path, notice: 'Main search was successfully created.'
else
render action: 'new'
end
end
end
As shown in railscast.
view / main_searches / _form.html.haml
%h1 Advanced Search
= form_for :main_search, :url => main_searches_path do |f|
.field
= f.label :id_search
%br/
= f.text_field :id_search
[... ommitted some fields here ...]
.field
= f.label :active
%br/
= f.check_box :active, {}, true, false
.actions= f.submit "Search"
Displays in a new view.
view / main_searches / _results.html.haml
%h1 Search Results
.container-fluid
.row-fluid
.span4
%table.table{style: "table-layout:fixed"}
%thead
%tr
%th= "id"
%th= "name_sur"
%th= "name first"
%th= "a"
%div{style: "overflow-y:scroll; height: 200px"}
%table.table.table-striped{style: "table-layout:fixed"}
%tbody
- @main_search.pupils.each do |pupil|
%tr
%td= pupil.id
%td= link_to pupil.name_sur, pupil_path(pupil)
%td= pupil.name_first
%td= pupil.active
Displays the results.
So basically everything works for one model, as shown on railscast. What I need now is to allow the user to handle everything in the main_controller. At the moment, I cannot get the @main_search object passed to the _results.html.haml particle. What am I missing here? Or is this even the correct way to do such a search?
Thanks for your help in advance.
source to share
Brians' solution actually worked well enough. I created a separate model with no corresponding database and put all the search logic in this file. Note that this is only a class and is not associated with ActiveRecord.
class MainSearch
def self.for(id_search, name_sur, name_first, active)
#escape for security
name_sur = "%#{name_sur}%"
name_first = "%#{name_first}%"
active = "%#{active}%"
pupils = Pupil.order(:name_sur)
pupils = pupils.where(id: id_search) if id_search.present?
pupils = pupils.where("name_first like ?", "%#{name_first}%") if name_first.present?
pupils = pupils.where("name_sur like ?", "%#{name_sur}%") if name_sur.present?
pupils = pupils.where(active: "true") if active == true #show only active or all
end
end
Now my main_controller can call this function and pass values ββfrom the params hash:
@pupils = MainSearch.for(params[:id_search], params[:name_sur], params[:name_first], params[:active])
The parameters come from views / main / _form_search.html.haml:
.search-box
= form_tag root_path, method: :get do
= label_tag :id_search
%br/
= text_field_tag 'id_search', nil
%br/
= label_tag :name_sur
%br/
= text_field_tag 'name_sur', nil
%br/
= label_tag :name_sur
[...]
= submit_tag "Submit"
The last step is to show the results. As you can see in the main_controller, I just populate the @pupils object and thus can easily display it in any view. Here is an excerpt from views / main / index.html.haml
.span8
.row-fluid
%table.table{style: "table-layout:fixed"}
%thead
%tr
%th= "id"
%th= sortable "name_sur"
%th= "name first"
%th= "a"
%div{style: "overflow-y:scroll; height: 200px"}
%table.table.table-striped{style: "table-layout:fixed"}
%tbody
- @pupils.each do |pupil|
%tr
%td= pupil.id
%td= link_to pupil.name_sur, pupil_path(pupil)
%td= pupil.name_first
%td= pupil.active
= render :partial => 'form_search'
This setting saves all controls in main_controller and only requires one external file (search model). In this model, I can search data across multiple models (as shown in the video) and combine the search criteria as I please, and even make some performance synonyms there. I am quite happy with the result. SO thanks for all the input.
source to share