Rails 4 How to Search by Order id or Order Status - ruby-on-rails

I would like to be able to search orders by id or by order status.
My current code is working but not 100%.
There is a has_many relationship between the Order and the Order Status models.
The order status model has four entries:
1.Processing , 2.Completed, 3.Cancelled, 4.Refunded
Example:
I have a simple form to enter the order id and another form with a select tag
with all the Order Statuses to search by order status.
If I search for order id 1, I get all the orders with a number 1 in the id and I also get all the orders with order status 1.
I would like get a result of either order id or order status.
Order model
class Order < ActiveRecord::Base
belongs_to :order_status
def self.search(search)
where("cast(id as text) LIKE ? OR order_status_id LIKE ? ", "%#{search}%", "%#{search}%")
end
end
Order Status model
class OrderStatus < ActiveRecord::Base
has_many :orders
end
Orders Controller
def index
#order_statuses = OrderStatus.all
if params[:search]
#orders = Order.search(params[:search])
else
#orders = Order.all
end
end
Order index view
<div class="row">
<div class="col-sm-6 form-group">
<%= form_tag orders_path, :method => 'get', class:"" do %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", :name => nil, class:"btn btn-primary" %>
</p>
<% end %>
</div>
<div class="col-sm-6 form-group">
<%= form_tag orders_path, :method => 'get', class:"" do %>
<p>
<%= select_tag :search, options_from_collection_for_select(OrderStatus.all, :id, :name, params[:search]) %>
<%= submit_tag "Search", :name => nil, class:"btn btn-primary" %>
</p>
<% end %>
</div>
</div>
<table class="table">
<tr>
<th>Order Id</th>
<th>amount</th>
<th>status</th>
<th>Last Updated</th>
<th>manage</th>
</tr>
<% #orders.each do |order| %>
<tr>
<td><%= order.id %></td>
<td><%= order.total %></td>
<td><%= order.order_status.name %></td>
<td><%= order.updated_at %></td>
<td><%= link_to 'details', order %> |
<%= link_to 'edit', edit_order_path(order) %> |
<%= link_to 'delete', order_path(order), method: :delete, data: { confirm: 'Are you sure?' } %>
</td>
</tr>
<% end %>
<tr><td colspan="4"></td></tr>
</table>
I've researched the following links but I'm not finding or understanding a solution.
Links:
http://railscasts.com/episodes/37-simple-search-form?autoplay=true
http://railscasts.com/episodes/111-advanced-search-form-revised?autoplay=true
http://guides.rubyonrails.org/active_record_querying.html
Rails Search Form
Build static dropdown menu for simple search Rails 3

You can do a search by either order_id or order_status :name attribute with the following
class Order < ActiveRecord::Base
belongs_to :order_status
def self.search(query)
query.to_s.is_i? ? where(id: query) : joins(:order_status).where('order_statuses.name LIKE ?', "%#{query}%")
end
end
In order to use the is_i? method you will need to extend the String class by creating an initializer file to require a new String extension file
# config/initializers/core_extensions.rb
Dir[File.join(Rails.root, "lib", "core_extensions", "*.rb")].each {|f| require f }
and then create your new String extension file and is_i? method
# lib/core_extensions/string.rb
class String
def is_i?
/\A[-+]?\d+\z/ === self
end
end
credit this SO answer for the is_i? method

There's no need to cast the id into text and then using like to match it. This should be enough to get what you need.
class Order < ActiveRecord::Base
belongs_to :order_status
def self.search(search)
where("id = ? OR order_status_id LIKE ? ", search, "%#{search}%")
end
end

Related

how can i get project_id by remarks in ruby on rails

I have manager remark model that takes input as a remark and decision value and saves it with the project site ID. I have a project site model that takes input as name, date, and file and stores it. Many remarks have a many to one relation with project site ID, and the project site belongs to the manager remark. I want to access the decision attribute boolean value in project site index form, but I am unable to access that boolean value in the index page of the project site. Here is my code of project site and manager remarks model, view and controller-
project site index.html.erb
<table>
<thead>
<tr>
<th>Name</th>
<th>Date</th>
<th>Attendance</th>
<th>Status</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% #project_sites.each do |project_site| %>
<tr>
<td><%= project_site.name.titleize %></td>
<td><%= project_site.date %></td>
<td><%= link_to ' View attendance', project_site.file, :class => "fi-page-export-csv" %></td>
<td><%= "here i want to access manager remark decision value" %></td>
<td><%= link_to 'Remark ', project_site %><span>(<%= project_site.manager_remarks.size %>)</span></td>
<td><%= link_to 'Edit', edit_project_site_path(project_site) %></td>
<td><%= link_to 'Destroy', project_site, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
project site controller
def index
#project_sites = ProjectSite.all.order("created_at DESC")
#manager_remark = ManagerRemark.joins(:project_site).where(:project_sites => { :user_id => #user.id })
end
# GET /project_sites/1
# GET /project_sites/1.json
def show
#manager_remark = ManagerRemark.new
#manager_remark.project_site_id = #project_site.id
end
# GET /project_sites/new
def new
#project_site = ProjectSite.new
end
def project_site_params
params.require(:project_site).permit(:name, :date, :file)
end
manager_remark controller
class ManagerRemarksController < ApplicationController
def create
#manager_remark = ManagerRemark.new(remark_params)
#manager_remark.project_site_id = params[:project_site_id]
#manager_remark.save
redirect_to project_site_path(#manager_remark.project_site)
end
def remark_params
params.require(:manager_remark).permit(:remark, :decision)
end
end
manager_remark view form
<%= form_for [ #project_site, #manager_remark ] do |f| %>
<div class="row">
<div class="medium-6 columns">
<%= f.radio_button :decision, true %>
<%= f.label :approve %>
<%= f.radio_button :decision, false %>
<%= f.label :reject %>
</div>
<br>
<br>
<div class="medium-6 cloumns">
<%= f.label :remark %><br/>
<%= f.text_area :remark %>
</div>
</div>
<div>
<%= f.submit 'Submit', :class => 'button primary' %>
</div>
<% end %>
routes.rb
Rails.application.routes.draw do
root to: 'home#index'
devise_for :users
resources :project_sites do
resources :manager_remarks
end
get '/project_manager_level_two' => 'project_manager_level_two#index'
get '/project_managers' => 'project_managers#index'
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
If I understand correctly, you have a ProjectSite that contains a ManagerRemark with a decision, right? If that's the case, the simple answer is:
<%= project_site.ManagerRemark.decision %>
If you are saying that each ProjectSite has many ManagerRemarks, you'll want to place the above inside a loop, like so:
<% project_site.manager_remarks.each do |manager_remark| %>
<%= manager_remark.decision %><br/>
<% end %>
This assumes that your models are correctly configured to recognize these relationships. The above may also be optimized by adding an include clause to your fetch inside the controller and there's no need to fetch the ManagerRemark objects separately. Therefore, you'd probably want something like:
def index
#project_sites = ProjectSite.all.includes( :manager_remark ).order("created_at DESC")
end

What Controls the Mapping of the Rails Controller Method to the View Rendered?

I have a rails application which is not routing as I expected. The search method in the controller is rending show. I've cut down the code to the minimal components and I am posting them here as suggested.
Rails.application.routes.draw do
resources :backups
get 'backups/search' => 'backups#search'
resources :components
resources :backup_media
end
class Component < ActiveRecord::Base
has_many :backups
has_many :backup_media, :through => :backups
end
class BackupMedium < ActiveRecord::Base
has_many :backups
has_many :components, :through => :backups
end
class Backup < ActiveRecord::Base
belongs_to :component
belongs_to :backup_medium
# value to match either the name of the component or backup_medium
def self.search(value)
tables = "backups, components, backup_media"
joins = "backups.backup_medium_id = backup_media.id and components.id = backups.component_id"
c = find_by_sql "select * from #{tables} where components.name like '%#{value}%' and #{joins}"
b = find_by_sql "select * from #{tables} where backup_media.name like '%#{value}%' and #{joins}"
c.count > 0 ? c : b
end
end
class BackupsController < ApplicationController
def search
#backups = Backup.search(params[:search])
render 'index'
end
def index
#backups = Backup.all
end
def show
# this would normally be the code to show an individual backup
# but I'm re-using the code from index because the routing is broken
#backups = Backup.all
end
end
views/backups/_search.html.erb
<%= form_tag backups_search_path, :method => 'get' do %>
<%= label_tag(:search, "Search for:") %>
<%= text_field_tag :search, params[:search], {:placeholder => 'Component or Media' }%>
<%= submit_tag "Search", :name => nil %>
<% end %>
views/backups/index.html.erb
<h1>Listing Backups</h1>
<p id="notice"><%= notice %></p>
<%= render :partial => 'search' %>
<table>
<tr>
<th>id</th>
<th>component_id</th>
<th>backup_medium_id</th>
</tr>
<% #backups.each do |backup| %>
<tr>
<td><%= backup.id %></td>
<td><%= backup.component.name %></td>
<td><%= backup.backup_medium.name %></td>
</tr>
<% end %>
</table>
views/backups/show.html.erb is copied from index.html.erb since it is incorrectly receiving the search results
<h1>Show Backup</h1>
<p id="notice"><%= notice %></p>
<%= render :partial => 'search' %>
<table>
<tr>
<th>id</th>
<th>component_id</th>
<th>backup_medium_id</th>
</tr>
<% #backups.each do |backup| %>
<tr>
<td><%= backup.id %></td>
<td><%= backup.component.name %></td>
<td><%= backup.backup_medium.name %></td>
</tr>
<% end %>
</table>
Suggestions on improving the search method will be welcomed.
As mentioned above, after the search is executed, the show.html.erb is rendered instead of search.html.erb
For a working demo (with better code thanks to suggestions here) see
https://github.com/pamh09/rails-search-demo
You do not have a backups_search_path in your routes, therefore it is treating search in the query string as an id and thus rendering show.html.erb, so try
get 'backups/search' => 'backups#search', as: :backups_search
In debugging I found that rails consistently routed to the wrong view when it was unhappy with the return object coming from the model.

Rails: how to render nested form in table

For example, I have two models:
class Task < ApplicationRecord
has_many :task_details
end
class TaskDetail < ApplicationRecord
belong_to :task
end
I want to display a table, each row in table is one TaskDetail and allow user input. After that user submits, all data will put to server. Here is my code:
(Note that: I #data[:task] is a task object because I want to return a hash with some information for view)
<%= form_for #data[:task], :url => tasks_path do |f| %>
<table> ... </table>
<% end %>
My question is: How can I do as my requirement.
thanks
Ensure that your Task model has accepts_nested_attributes_for :task_details and then you can do something like...
<%= form_for #data[:task], :url => tasks_path do |f| %>
<table>
<tr>
<th>Task Name</th>
<th>Task Description</th>
<tr>
<%= f.fields_for :task_details do |task_detail| %>
<tr>
<%= task_detail.hidden_field :id %>
<td><%= task_detail.text_field :name %></td>
<td><%= task_detail.text_field :description %> </td>
<tr>
<% end %>
</table>
<% end %>
Note the use of the hidden field for :id ... you need that so that rails can distinguish data from existing tasks versus a new task you're entering.
In your new method you should ensure there's at least one new task detail to provide an empty line on the form to input the detail
def new
...
#data[:task].task_details.build
...
end

Rails 4 Active Record Querying

I'm querying orders by date range.
It is working now but I have the logic in the controller not in the model. I know that this should be in the model instead.
I've tried several approaches but I've had no luck.
Question:
What is the proper way of doing this?
I'm looking at the Rails Guides Active Record Querying section 2.2 Array Conditions:
http://guides.rubyonrails.org/active_record_querying.html
Also researched several question here in Stack overflow:
undefined local variable or method `params' for #<Result:0x3904b18>
undefined local variable or method `user_params' rails 4
ruby query between two date parameters
orders.rb Empty now to avoid errors
def self.search_range
end
orders_controller.rb
def search_range
#orders = Order.where("created_at >= :start_date AND created_at <= :end_date",{start_date: params[:start_date], end_date: params[:end_date]}).order("created_at desc")
end
search_range.html.erb Here I'm entering the date range
<div class="container-fluid events-container">
<div class="row">
<div class="col-sm-12">
<h1>Orders</h1>
</div>
</div>
<div class="row">
<div class="col-sm-6 form-group">
<%= form_tag search_range_path, :method => 'get', class:"" do %>
<p>
<%= text_field_tag :start_date, params[:start_date] %>
<%= text_field_tag :end_date, params[:end_date] %>
<%= submit_tag "Search", :name => nil, class:"btn btn-primary" %>
</p>
<% end %>
</div>
</div>
<table class="table">
<tr>
<th>amount</th>
<th>status</th>
<th>Last Updated</th>
<th>Order Id</th>
<th>manage</th>
</tr>
<% #orders.each do |order| %>
<tr>
<td><%= order.total %></td>
<td><%= order.order_status.name %></td>
<td><%= order.created_at.strftime("%m/%d/%Y") %></td>
<td><%= order.id %></td>
<td><%= link_to 'details', order %> |
<%= link_to 'edit', edit_order_path(order) %> |
<%= link_to 'delete', order_path(order), method: :delete, data: { confirm: 'Are you sure?' } %>
</td>
</tr>
<% end %>
<tr><td colspan="4"></td></tr>
</table>
<div class="row">
<div class="col-sm-12">
<hr>
</div>
</div>
</div>
I took the liberty to also eager load the association, order_status, because in your view it looks like you are referencing that table. Here is a solid guide on eager loading.
Below should get you to your solution. Good for you for writing the correct query in your controller, and recognizing the need to refactor to your model. The below is also not tested, so let me know if it doesn't produce the results you need. The code below can also lead to errors, if the params are not valid dates, so you may want to go further to ensure it correctly handles errors.
# controller
#orders = Order.includes(:order_status).
filter_between_dates(params[:start_date], params[:end_date]).
recent
# model
scope :filter_between_dates, (lambda do |start_date, end_date|
return all unless start_date.present? && end_date.present?
where('created_at >= ? AND created_at <= ?', start_date, end_date)
end)
# Order based on created_at date.
#
# examples:
# Order.recent
# Order.recent('asc')
scope :recent, -> (default = 'desc') { order(created_at: default.to_sym) }
You could even go one step further and refactor this using the query object pattern.
# app/finders/orders/search_finder.rb
module Orders
class SearchFinder
attr_reader :params
def initialize(params)
#params = params
end
def execute
Order.includes(:order_status).
filter_between_dates(params[:start_date], params[:end_date]).
recent
end
end
end
# controller
#orders = Orders::SearchFinder.new(params).execute

undefined method for nil:NilClass error

I'm receiving an undefined method `site_name' for nil:NilClass error for the following. Would appreciate any help. I'm trying to list the site_name in the Site table and i'm not sure how best to resolve the issue.
class Site < ActiveRecord::Base
attr_accessible :site_id, :site_name, :region
has_many :trials, :foreign_key => "site_id"
end
class Trial < ActiveRecord::Base
attr_accessible :trial_id, :site_id, :year, :trial_type
scope :year, ->(year) { where(year: year) }
scope :trial_id, ->(trial_id) { where(trial_id: trial_id) }
belongs_to :site
end
My Controller:
class TrialsController < ApplicationController
def index
#years = Trial.group('year').order('year DESC')
end
def trial
#trial = Trial.trial_id(params[:trial_id])
end
**def list
#list = Trial.year(params[:year]).order(:region_id).joins(:sites).where('sites.site_id' => :site_id)
end**
end
My view:
<% #list.each do |list| %>
<tr>
<td>
<%= link_to list.site.site_name, trial_trials_path(trial_id: list.trial_id) %>
</td>
<td>
<%= link_to list.trial_type, trial_trials_path(trial_id: list.trial_id) unless list.trial_type.blank? %>
</td>
<td>
<%= link_to list.trial_type, trial_trials_path(trial_id: list.trial_id) %>
</td>
<td>
<%= link_to list.planted_at, trial_trials_path(trial_id: list.trial_id) unless list.planted_at.blank? %>
</td>
<td>
<%= link_to list.picked_at, trial_trials_path(trial_id: list.trial_id) unless list.picked_at.blank? %>
</td>
</tr>
<% end %>
What if you just change the line to this:
<%= link_to list.site.site_name, trial_trials_path(trial_id: list.trial_id) if list.site.try(:site_name) %>
Also, you could do a Site.where(site_name: nil) in the Rails console to see which site doesn't have a name.
if you don't want page crush even if there is not site for list, you could try to use this
list.site.try(:site_name)
but I think that you should use another flow, like
Do not let to create list without site.
Do not list lists without sites.
You have to decide about your default site name, eg. "Example.com". And then you simply define it on nil:
def nil.site_name
"Example.com"
end
And the error will go away.

Resources