I have generated Admin namespaced Controllers for all my default models as follows:
rails g scaffold_controller admin/categories name:string slug:string description:string icon_xlarge:string icon_large:string icon_medium:string icon_small:string status:integer
This generated the following files:
Harshas-MacBook-Pro:nomad harshamv$ rails g scaffold_controller admin/categories name:string slug:string description:string icon_xlarge:string icon_large:string icon_medium:string icon_small:string status:integer
Plural version of the model detected, using singularized version. Override with --force-plural.
create app/controllers/admin/categories_controller.rb
invoke erb
create app/views/admin/categories
create app/views/admin/categories/index.html.erb
create app/views/admin/categories/edit.html.erb
create app/views/admin/categories/show.html.erb
create app/views/admin/categories/new.html.erb
create app/views/admin/categories/_form.html.erb
invoke test_unit
create test/controllers/admin/categories_controller_test.rb
app/model/category.rb
class Category < ActiveRecord::Base
extend FriendlyId
friendly_id :name, use: :slugged
has_and_belongs_to_many :venues
end
app/controller/admin/categories_controller.rb
class Admin::CategoriesController < ApplicationController
before_action :set_admin_category, only: [:show, :edit, :update, :destroy]
# GET /admin/categories
def index
#admin_categories = Admin::Category.all
end
# GET /admin/categories/1
def show
end
# GET /admin/categories/new
def new
#admin_category = Admin::Category.new
end
# GET /admin/categories/1/edit
def edit
end
# POST /admin/categories
def create
#admin_category = Admin::Category.new(admin_category_params)
if #admin_category.save
redirect_to #admin_category, notice: 'Category was successfully created.'
else
render :new
end
end
# PATCH/PUT /admin/categories/1
def update
if #admin_category.update(admin_category_params)
redirect_to #admin_category, notice: 'Category was successfully updated.'
else
render :edit
end
end
# DELETE /admin/categories/1
def destroy
#admin_category.destroy
redirect_to admin_categories_url, notice: 'Category was successfully destroyed.'
end
private
# Use callbacks to share common setup or constraints between actions.
def set_admin_category
#admin_category = Admin::Category.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def admin_category_params
params.require(:admin_category).permit(:name, :slug, :description, :icon_xlarge, :icon_large, :icon_medium, :icon_small, :status)
end
end
app/view/admin/categories/index.html.erb
<h1>Listing admin_categories</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>Slug</th>
<th>Description</th>
<th>Icon xlarge</th>
<th>Icon large</th>
<th>Icon medium</th>
<th>Icon small</th>
<th>Status</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% #admin_categories.each do |admin_category| %>
<tr>
<td><%= admin_category.name %></td>
<td><%= admin_category.slug %></td>
<td><%= admin_category.description %></td>
<td><%= admin_category.icon_xlarge %></td>
<td><%= admin_category.icon_large %></td>
<td><%= admin_category.icon_medium %></td>
<td><%= admin_category.icon_small %></td>
<td><%= admin_category.status %></td>
<td><%= link_to 'Show', admin_category %></td>
<td><%= link_to 'Edit', edit_admin_category_path(admin_category) %></td>
<td><%= link_to 'Destroy', admin_category, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Category', new_admin_category_path %>
My Attempts
I edited the Controller as below
# GET /admin/categories
def index
#admin_categories = Category.all
end
# GET /admin/categories/1
def show
end
# GET /admin/categories/new
def new
#admin_category = Category.new
end
# GET /admin/categories/1/edit
def edit
end
# POST /admin/categories
def create
#admin_category = Category.new(admin_category_params)
if #admin_category.save
redirect_to #admin_category, notice: 'Category was successfully created.'
else
render :new
end
end
When I go to localhost/admin/categories and click "NEW category", I get the following error now:
My routes file:
Rails.application.routes.draw do
# Admin Routing
namespace :admin do
resources :categories, :cities, :countries, :lists, :oauths, :regions, :tags, :users, :user_groups, :venues, :venue_photos, :venue_reviews
end
end
You have resources :categories defined under the namespace :admin in your routes.rb,so this line in your views/admins/categories/_form.html.erb
<%= form_for(#admin_category) do |f| %>
should be
<%= form_for([:admin, #admin_category]) do |f| %>
For more info,refer this API
Update
The second error is because of this line
params.require(:admin_category).permit(:name, :slug, :description, :icon_xlarge, :icon_large, :icon_medium, :icon_small, :status)
It should be
params.require(:category).permit(:name, :slug, :description, :icon_xlarge, :icon_large, :icon_medium, :icon_small, :status)
To access models outside of the namespace you need to call ::Category.new instead of Admin::Category.new
As your error indicates, this is an issue with how you are calling your form. Your form needs to reference the admin namespace, like this:
<%= form_for [:admin, #category] do |f| %>
However, there are a number of things different about the way the scaffold built your docs from how I would recommend.
I would also suggest simplifying the code in the scaffolded controller to reference simple #category and #categories, rather than #admin_category and #admin_categories.
Also, the model should not be in the admin namespace, so Admin::Category.new should be Category.new. The rest of the model calls should be adjusted accordingly as well.
Related
I have a Rails 5.2.6 app with a PostgresQL db. Its purpose is to consume an API for cocktails, displaying cocktails that match a query. Each cocktail in the response has an "Add Favorite" button to save that particular cocktail into the local database. This would allow a list of favorites for each user that is local.
Users is set up with Devise gem.
There are 2 Classes, Pages and Cocktails. Cocktails has full CRUD and can add drinks without using the API. The CRUD for the Cocktails class is fully tested and works. It successfully creates new cocktails without using the api.
Where I'm at now is consuming the API with a query, displaying the response in a list, and giving the user an "Add Favorite" button next to each entry to add a specific new drink to their favorites list that is tied to their user profile.
But I'm having trouble because I've never tried to create without a erb form.
After much research, I think I found the code to do it. However, I'm getting an error when I pass the params after hitting the "Add Favorite" button. The error is:
ActiveRecord::AssociationTypeMismatch in CocktailsController#create
User(#70044407074280) expected, got "6" which is an instance of String(#47022904663520)
"6" is actually the user id of the user I'm logged in as. But I think its not the same type?
Here is the view
<h1>Search for Cocktails</h1>
<%= form_tag(search_results_page_path, method: :get) do %>
<%= text_field_tag(:search, params[:search]) %>
<%= button_tag "Search", :class => 'btn', :name => nil %>
<% end %>
<% if #drinks.any? %>
<table>
<thead>
<tr>
<th>Image</th>
<th>Name</th>
<th>Alcohol</th>
<th>Type</th>
<th>Glass</th>
</tr>
</thead>
<% #drinks["drinks"].each do |drink| %>
<tr>
<th rowspan="3" scope="rowgroup"><%= image_tag drink["strDrinkThumb"], :class => 'drink-thumb' %></th>
<td><%= drink["strDrink"] %></td>
<td><%= drink["strAlcoholic"] %></td>
<td><%= drink["strCategory"] %></td>
<td><%= drink["strGlass"] %></td>
<td><%= button_to "Add Favorite", { :controller => "cocktails", :action => "create", :cocktail => {:name => drink["strDrink"], :category => drink["strCategory"], :alcoholic => drink["strAlcoholic"], :glass => drink["strGlass"], :instructions => drink["strInstructions"], :ingredients => drink["strIngredient1"], :api_id_drink => drink["idDrink"], :api_image_url => drink["strDrinkThumb"], :user => current_user }}, {:method => :post} %></td>
</tr>
<tr>
<td colspan="4" scope="colgroup" ><%= drink["strInstructions"] %> </td>
</tr>
<% i = 1 %>
<% while !drink["strIngredient" + i.to_s].nil? do %>
<tr>
<% if i > 1 %>
<td></td>
<% end %>
<td><%= drink["strIngredient" + i.to_s] %> </td>
<td><%= drink["strMeasure" + i.to_s] %> </td>
</tr>
<% i = i + 1 %>
<% end %>
<% end %>
<tbody>
</tbody>
</table>
<% end %>
<br>
<%= link_to 'Back to Favorites', root_path %>
Here's the cocktails controller
require 'drinks'
class CocktailsController < ApplicationController
before_action :set_cocktail, only: %i[ show edit update destroy ]
# GET /cocktails or /cocktails.json
def index
#cocktails = current_user.cocktails
render :index
end
# GET /cocktails/1 or /cocktails/1.json
def show
end
# GET /cocktails/new
def new
#cocktail = Cocktail.new
render :new
end
# GET /cocktails/1/edit
def edit
end
# POST /cocktails or /cocktails.json
def create
#cocktail = current_user.cocktails.build(cocktail_params)
if #cocktail.save
redirect_to #cocktail, notice:'Cocktail was successfully created.'
else
render :new
end
end
# PATCH/PUT /cocktails/1 or /cocktails/1.json
def update
respond_to do |format|
if #cocktail.update(cocktail_params)
format.html { redirect_to #cocktail, notice: "Cocktail was successfully updated." }
format.json { render :show, status: :ok, location: #cocktail }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: #cocktail.errors, status: :unprocessable_entity }
end
end
end
# DELETE /cocktails/1 or /cocktails/1.json
def destroy
#cocktail.destroy
respond_to do |format|
format.html { redirect_to cocktails_url, notice: "Cocktail was successfully destroyed." }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_cocktail
#cocktail = Cocktail.find(params[:id])
end
# Only allow a list of trusted parameters through.
def cocktail_params
params.require(:cocktail).permit(:name, :category, :alcoholic, :glass, :instructions, :ingredients, :user)
end
end
This is the Pages Controller, it was meant to handle the search and the api call, it has no model (its is accessing the api call drinks.rb which is in /services using httparty)
require 'drinks'
class PagesController < ApplicationController
def search
render :search
end
def search_results
#search = params[:search]
api_call = Drink.new(#search.strip)
response = api_call.drink_name
api_error_handler(response)
render :search_results
end
private
def api_error_handler(response)
#drinks = JSON(response.body)
if #drinks["drinks"].blank?
flash[:alert] = "We couldn't find that cocktail"
return #drinks = {}
end
case response.code
when 200
flash[:alert] = "Search Complete"
when 404
flash[:alert] = "Sorry, That cocktail wasn't found."
#drinks = {}
when 400..403
flash[:alert] = "Error #{response.code}"
#drinks = {}
when 405..499
flash[:alert] = "Error #{response.code}"
#drinks = {}
when 500..600
flash[:alert] = "Server Error #{response.code}. Please try again a little later."
#drinks = {}
else
flash[:alert] = "Unknown Error"
#drinks = {}
end
end
end
Here's the Cocktail model:
require 'httparty'
class Cocktail < ApplicationRecord
belongs_to :user
validates :name, presence: true
validates :category, presence: true
validates :alcoholic, presence: true
validates :instructions, presence: true
validates :ingredients, presence: true
validates_length_of :instructions, maximum: 500
validates_length_of :ingredients, maximum: 300
def self.api_call
#drinks = HTTParty.get('http://www.thecocktaildb.com/api/json/v1/1/search.php?s=margarita')
end
end
In the error message I get, these are the listed params:
Application Trace | Framework Trace | Full Trace
app/controllers/cocktails_controller.rb:29:in `create'
Request
Parameters:
{"authenticity_token"=>"DQPbxSRMcJgL+xX5JDzL6arDG/yQ7s5J+eBTmwtpnbKCV7SMbJI7EJ/9UoH55z8o+lL8XQsTUUICHAgtlzVwkQ==",
"cocktail"=>
{"alcoholic"=>"Alcoholic",
"api_id_drink"=>"17141",
"api_image_url"=>"https://www.thecocktaildb.com/images/media/drink/rx8k8e1504365812.jpg",
"category"=>"Punch / Party Drink",
"glass"=>"Beer mug",
"ingredients"=>"Red wine",
"instructions"=>"Throw it all together and serve real cold.",
"name"=>"Smut",
"user"=>"6"}}
So it like its mostly working to create the new cocktail, just a problem with the :user param. I tried using each of the following in the button params:
:user => session[:user_id]
:user => current_user
:user => current_user.id
I can't seem to find the answer.
With Devise, current_user is accessible from your controllers. So, there's no need to try and pass in a user object or id as a param from the view. Removing user as a param will fix the error.
I have users that have posts.
<p id="notice"><%= notice %></p>
<h1>Listing Posts</h1>
<table>
<thead>
<tr>
<th>Comment</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% #posts.each do |post| %>
<tr>
<td><%= post.content %></td>
<td><%= link_to 'Show', post %></td>
<td><%= link_to 'Edit', edit_post_path(post) %></td>
<td><%= link_to 'Destroy', post, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Post', new_user_post_path %>
And in controller
def destroy
#user = #post.user
#post.destroy
respond_to do |format|
format.html { redirect_to user_posts_url(#user), notice: 'Post was successfully destroyed.' }
format.json { head :no_content }
end
end
What's the proper way to implement a link and controller action to destroy all posts for a particular user?
Edit:
config/routes.rb
resources :users do
resources :posts, shallow: true
end
Edit 2:
resources :users do
#resources :posts, shallow: true
resources :posts, shallow: true do
delete :destroy_all, on: collection
end
end
gives no block given (yield) error
aww my bad.. Just found the error.. forgot to add : to collection
I would pass an array of post IDs only if selected posts need to be deleted. If you want to delete all posts for a particular user, then here's how I would approach it:
config/routes.rb
resources :users do
resources :posts do
delete :destroy_all, on: :collection
end
end
Here, on: :collection means that the route applies to the collection of posts; the route therefore looks like this:
/users/:user_id/posts/destroy_all
You can read more about adding member and collection routes in the Rails Guides:
http://guides.rubyonrails.org/routing.html#adding-more-restful-actions
app/controllers/posts_controller.rb
def destroy_all
user = User.find(params[:user_id])
user.posts.destroy_all
# redirect somewhere
end
app/views/posts/index.html.erb
<%= link_to(
"Delete all posts!",
destroy_all_user_posts_path,
method: :delete
) %>
If you want to delete all posts for the current_user, modify like so:
config/routes.rb
resources :posts do
delete :destroy_all, on: :collection
end
app/controllers/posts_controller.rb
def destroy_all
current_user.posts.destroy_all
# redirect somewhere
end
app/views/posts/index.html.erb
<%= link_to(
"Delete all posts!",
destroy_all_posts_path,
method: :delete
) %>
Hope that helps.
I would create a separate controller method that accepts an array of post ids.
posts_controller.rb
def destroy_all
posts = Post.where(:id => params[:post_ids])
posts.delete_all
redirect_to :back
end
You will also need to supply the ids to the view method.
posts_controller.rb
def index
...
#posts_ids = Post.find(... how ever you need to select all posts...).pluck(:id)
...
end
views/posts/index.html.erb
...
<%= link_to destroy_all_posts_path(:post_ids => #posts_ids), :method => :destroy %>
...
You will also need to supply the route.
routes.rb
resources :users do
resources :posts
delete :destroy_all
end
end
And that should be it :)
You can use:
def destory_posts(user)
user.posts.destroy_all
render :nothing => true
end
add this method to your routes file.
Create a link like destory_posts_path(current_user) from where you want to delete the posts.
In my Ruby on Rails application users are able to leave reviews for products, and then the administrator can view all reviews. What I want to be able to do is make it so that an administrator can still see all reviews left by everyone but a normal user can only view their own reviews and not everyone elses. Is there a simple way I can do this without using java?
Below is my index.html.erb that displays all reviews by everyone.
<div class="centre-content">
<div class="main-title">All reviews:</div>
<table>
<tr>
<th>Product</th>
<th>Name</th>
<th>Review text</th>
<th>No of stars</th>
<th></th>
<th></th>
<th></th>
</tr>
<% #reviews.each do |review| %>
<tr>
<td><%= review.product.title %></td>
<td><%= review.user.name %></td>
<td><%= review.review_text.truncate(35) %></td>
<td><%= review.no_of_stars %></td>
<td><%= link_to 'Show', review %></td>
<td><%= link_to 'Edit', edit_review_path(review) %></td>
<td><%= link_to 'Destroy', review, confirm: 'Are you sure?', method: :delete %></td>
</tr>
<% end %>
</table>
<br />
<%= link_to 'Back', :back %>
</div>
application.html.erb where the administrator can click to view all reviews:
<% if admin? %>
<li> <%= link_to "Users", "" , :class => active_menu("users") %>
<ul>
<li> <%= link_to "Users" , users_path %> </li>
<li> <%= link_to "Edit profile" , edit_user_path(session[:user_id]) %> </li>
</ul>
<li> <%= link_to "Categories", categories_path , :class => active_menu("categories") %>
<li> <%= link_to "Reviews", reviews_path , :class => active_menu("reviews") %>
<% end %>
reviews_controller.rb:
class ReviewsController < ApplicationController
before_action :set_review, only: [:show, :edit, :update, :destroy]
# GET /reviews
# GET /reviews.json
def index
#reviews = Review.all
end
# GET /reviews/1
# GET /reviews/1.json
def show
end
def new
if logged_in?
existing_review = Review.find_by_user_id_and_product_id(session[:user_id], params[:id])
if existing_review == nil
#review = Review.new(product_id: params[:id],
user_id: User.find(session[:user_id]).id)
session[:return_to] = nil
else
redirect_to edit_review_path(existing_review.id)
end
else
session[:return_to] = request.url
redirect_to login_path, alert: "You need to login to write a review"
end
end
# GET /reviews/1/edit
def edit
end
def create
#review = Review.new(review_params)
if #review.save
product = Product.find(#review.product.id)
redirect_to product, notice: 'Your review was successfully added.'
else
render action: 'new'
end
end
# PATCH/PUT /reviews/1
# PATCH/PUT /reviews/1.json
def update
respond_to do |format|
if #review.update(review_params)
format.html { redirect_to #review, notice: 'Review was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #review.errors, status: :unprocessable_entity }
end
end
end
# DELETE /reviews/1
# DELETE /reviews/1.json
def destroy
#review.destroy
respond_to do |format|
format.html { redirect_to reviews_url }
format.json { head :no_content }
end
end
def displays
product = Product.find(params[:id])
#reviews = product.reviews
if #reviews.empty?
redirect_to product, notice: "No reviews - as yet ..."
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_review
#review = Review.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def review_params
params.require(:review).permit(:product_id, :user_id, :review_text, :no_of_stars)
end
end
reviews.rb model:
class Review < ActiveRecord::Base
belongs_to :product
belongs_to :user
validates :review_text, :presence => { :message => "Review text: cannot be blank ..."}
validates :review_text, :length => {:maximum => 2000, :message => "Review text: maximum length 2000 characters"}
validates :no_of_stars, :presence => { :message => "Stars: please rate this book ..."}
end
Is there anyway I can make it so that a user only views their own reviews?
In your controller display method, you can do stg like this i think :
def display
product = Product.find(params[:id])
#reviews = product.reviews.select! { |s| s.user_id == current_user.id } unless admin?
....
end
That way, only admin will have all reviews, and others users will only see their own reviews (with theirs ids)
you dont have current_user method but it es explained well in this tutorial :
https://www.railstutorial.org/book/log_in_log_out#sec-current_user
it is the same as session[:id] but in a well ordered manner.
Ps: i'm not sur about the s.user_id ... don't remember the exact syntax but should be it.
Another way :
you could take the reviews for this specific product id and then filter out by the user id.
`#reviews = Reviews.find_by_product_id(params[:product_id]).select { |r| r.user_id == current_user.id }`
What I Want:
I need in a view a button or a link (it doesn't matter) to the create action of Reservation controller and to give it a parameter too.
And resolve the ForbiddenAttributesError that now gives me.
Here are my model and controller:
Reservation model
class Reservation < ActiveRecord::Base
belongs_to :user
belongs_to :dinner
end
Reservation controller
class ReservationsController < ApplicationController
load_and_authorize_resource
def show
#reservations = Reservation.joins(:user).where('dinner_id' => params[:dinner_id]).select("users.*,reservations.*")
#dinnerid = params[:dinner_id]
respond_to do |format|
format.html
format.json { render :json => #reservations }
end
end
def create
#reservation = Reservation.new(params[:reservation])
#reservation.user_id = current_user.id
respond_to do |format|
if #reservation.save
format.html { redirect_to #reservation, notice: 'Reservation was successfully created.' }
format.json { render :show, status: :created, location: #reservation }
else
format.html { render :show }
format.json { render json: #reservation.errors, status: :unprocessable_entity }
end
end
end
# Never trust parameters from the scary internet, only allow the white list through.
def reservation_params
params.require(:reservation).permit(:dinner_id)
end
end
EDIT: After the suggestion of #Rahul Singh this is my actual code with relative error:
<p id="notice"><%= notice %></p>
<table>
<thead>
<tr>
<th>Id</th>
<th>User id</th>
<th>Dinner id</th>
<th>User email</th>
<th>User name</th>
</tr>
</thead>
<tbody>
<% #reservations.each do |reservation| %>
<tr>
<td><%= reservation.id %></td>
<td><%= reservation.user_id %></td>
<td><%= reservation.dinner_id %></td>
<td><%= reservation.user.email %></td>
<td><%= reservation.user.name %></td>
</tr>
<% end %>
</tbody>
</table>
<br/>TRY 00a <br/>
<%= form_for(Reservation.new) do |f| %>
<%= f.hidden_field( :dinner_id, :value => #dinnerid.to_s) %>
<%= f.submit "Join1" %>
<% end %>
<br/> !!!!!!!!!!ERROR : ActiveModel::ForbiddenAttributesError
<br/>TRY 00b <br/>
<%= link_to "Join1", reservations_path(dinner_id:#dinnerid.to_s), method: :post %>
<br/> !!!!!!!!!!ERROR : param is missing or the value is empty: reservation
I provide a sreenshot for the error :
Error of the form : https://www.dropbox.com/s/i2x1m520ptqdj56/createReservationForm.jpg
Error of the link_to : https://www.dropbox.com/s/8xjwee5oo7q6uhk/createReservationLink_to.jpg
This should work
<%= form_for(Reservation.new) do |f| %>
<%= f.hidden_field( :dinner_id, :value => #dinnerid.to_s) %>
<%= f.submit "Join1" %>
<% end %>
clicking on Join1 button will submit form to ReservationsController create action.
and with link try this
<%= link_to "Join1", reservations_path(dinner_id:#dinnerid.to_s), method: :post %>
for above to work,add following in your routes.rb
resources :reservations
Change this line -> #reservation = Reservation.new(params[:reservation])
To this -> #reservation = Reservation.new reservation_params
and try again ;).
I'm wondering if it wouldn't be better to have your reservation routes as a nested resource of dinners.
It seems reservations can't exist without a dinner, so I'd make that explicit like this:
# config/routes.rb
resources :dinners do
resources :reservations
end
Run rake routes to see how this would change the routes.
You'd now have the dinner id passed along:
# app/views/dinners/show.html.erb
<%= button_to 'Reserve this dinner', dinner_reservations_path(#dinner) %>
The button would route to the create action because a button's default HTTP method is POST.
# app/controllers/reservations_controller.rb
class ReservationsController < ApplicationController
before_action :set_dinner
def create
#dinner.reservations.create! user: current_user
# would render reservations/create.html.erb
end
private
def set_dinner
#dinner = Dinner.find(params[:id])
end
end
This doesn't fix your immediate problem of just getting that link to work. But I think you'd be a lot better served structuring your app more like the above going forward.
Full disclosure: the person who asked this question contacted me on twitter personally, so I took some liberties in answering this question with a more general design suggestion.
I'm not sure this is the "best" approach but I think its the easiest one.
You could do something with string interpolation:
a href="/reservations?dinner=#{dinner.id}" Join
then you could get the paramter with
params[:dinner]
I'm having trouble understanding the best way to make a advanced search form. I have had a good search on the internet, looking at some ways, but I can't get them to work, as most of the suggestions are outdated. I have asked a question already, but I think I was too specific and I wasn't able to fix my problem. I am wanting to search on different text boxes and drop down boxes with one search button.
EDIT2:
projects_controller:
def index
#projects = Project.all
respond_to do |format|
format.html # index.html.erb
format.json { render :json => #projects }
end
end
def search
#project_search = Project.search(params[:search]).order(sort_column + ' ' + sort_direction).paginate(:per_page => 2, :page => params[:page])
end
# GET /projects/1
# GET /projects/1.json
def show
#project = Project.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #project }
end
end
# GET /projects/new
# GET /projects/new.json
def new
#project = Project.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #project }
end
end
# GET /projects/1/edit
def edit
#project = Project.find(params[:id])
end
# POST /projects
# POST /projects.json
def create
#project = Project.new(params[:project])
#project.client = params[:new_client] unless params[:new_client].blank?
#project.exception_pm = params[:new_exception_pm] unless params[:new_exception_pm].blank?
#project.project_owner = params[:new_project_owner] unless params[:new_project_owner].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
# PUT /projects/1
# PUT /projects/1.json
def update
#project = Project.find(params[:id])
respond_to do |format|
if #project.update_attributes(params[:project])
format.html { redirect_to #project, notice: 'Project was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
# DELETE /projects/1
# DELETE /projects/1.json
def destroy
#project = Project.find(params[:id])
#project.destroy
respond_to do |format|
format.html { redirect_to projects_url }
format.json { head :no_content }
end
end
private
helper_method :sort_column, :sort_direction
def sort_column
Project.column_names.include?(params[:sort]) ? params[:sort] : "project_name"
end
def sort_direction
%w[asc desc].include?(params[:direction]) ? params[:direction] : "asc"
end
end
Search View:
<h1>Search</h1>
<%= form_tag search_path, method: :get do %>
<%= hidden_field_tag :direction, params[:direction] %>
<%= hidden_field_tag :sort, params[:sort] %>
<%= text_field_tag :project_name, params[:project_name] %>
<%= text_field_tag :client, params[:client] %>
<%= submit_tag "Search", name: nil %>
<% end %>
<table class = "pretty">
<table border="1">
<tr>
<th><%= sortable "project_name", "Project name" %> </th>
<th><%= sortable "client", "Client" %></th>
<th>Exception pm</th>
<th>Project owner</th>
<th>Tech</th>
<th>Role</th>
<th>Industry</th>
<th>Financials</th>
<th>Business div</th>
<th>Status</th>
<th>Start date</th>
<th>End date</th>
<% if false %>
<th>Entry date</th>
<th>Edited date</th>
<th>Summary</th>
<th>Lessons learned</tStackh>
<th>Customer benifits</th>
<th>Keywords</th>
<!th></th>
<!th></th>
<!th></th>
<% end %>
</tr>
<% #project_search.each do |t| %>
<tr>
<td><%= t.project_name %></td>
<td><%= t.client %></td>
<td><%= t.exception_pm %></td>
<td><%= t.project_owner %></td>
<td><%= t.tech %></td>
<td><%= t.role %></td>
<td><%= t.industry %></td>
<td><%= t.financials %></td>
<td><%= t.business_div %></td>
<td><%= t.status %></td>
<td><%= t.start_date %></td>
<td><%= t.end_date %></td>
<% if false %>
<td><%= t.entry_date %></td>
<td><%= t.edited_date %></td>
<td><%= t.summary %></td>
<td><%= t.lessons_learned %></td>
<td><%= t.customer_benifits %></td>
<td><%= t.keywords %></td>
<% end %>
<!td><%#= link_to 'Show', project %></td>
<!td><%#= link_to 'Edit', edit_project_path(project) %></td>
<!td><%#= link_to 'Destroy', project, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</table>
<br />
<%= will_paginate (#project_search) %>
<%= button_to "Search Again?", search_path, :method => "get" %>
<%# end %>
<%= button_to "Home", projects_path, :method => "get" %>
Project.rb
class Project < ActiveRecord::Base
attr_accessible :business_div, :client, :customer_benifits, :edited_date, :end_date, :entry_date, :exception_pm, :financials, :industry, :keywords, :lessons_learned, :project_name, :project_owner, :role, :start_date, :status, :summary, :tech
validates_presence_of :business_div, :client, :customer_benifits, :end_date, :exception_pm, :financials, :industry, :keywords, :lessons_learned, :project_name, :project_owner, :role, :start_date, :status, :summary, :tech
def self.search search_term
return scoped unless search_term.present?
where find(:all, :conditions => ['project_name OR client LIKE ?', "%#{search_term}%"])
end
end
Routes:
FinalApp::Application.routes.draw do
resources :projects
match "search" => "projects#search", :as => :search
root :to => 'projects#index'
end
As you can see, I'm still a fair bit away from having a finished application. I am trying to make a search form that will be able to search on the following fields: Project name, Client, ID, Industry, Role, Technology, Project Owner, Status, Start Date, End Date, and Keywords. The search form would have either text boxes or drop down menus depending on which field the user was searching for. I am wanting to chain each field and search on them all in one go. Before, I was only using project_name, and client as examples to make it easier for you to understand my code. Hopefully you can see now what I am trying to do.
You can create a new controller called search.
Your search form:
<%= form_tag search_index_path, method: :get do %>
<%= text_field_tag :project, params[:project] %>
<%= text_field_tag :client, params[:client] %>
<%= submit_tag "Search", name: nil %>
<% end %>
incude in your routes.rb:
get "search/index"
your search controller:
def index
#store all the projects that match the name searched
#projects = Project.where("name LIKE ? ", "%#{params[:project]}%")
#store all the clients that match the name searched
#clients = Client.where("name LIKE ? ", "%#{params[:client]}%")
end
Now you can play with #projects and #clients in the index view.
Just be careful, because these variables might became nil if there is no match for the search.
EDIT - I am assuming you have two models Project and Client - if you cannot create a new controller you can create the search action in your current controller.
def search
#store all the projects that match the name searched
#projects = Project.where("name LIKE ? ", "%#{params[:project]}%")
#store all the clients that match the name searched
#clients = Client.where("name LIKE ? ", "%#{params[:client]}%")
end
And than you can use the #projects and #clients in the search view.
If you are trying to display the results in somewhere else (for example index view), you can just move the above to the correct action.
def index
....
#store all the projects that match the name searched
#projects = Project.where("name LIKE ? ", "%#{params[:project]}%")
#store all the clients that match the name searched
#clients = Client.where("name LIKE ? ", "%#{params[:client]}%")
end
EDIT 2 - OK, you are trying to search by a combination of fields in the same model:
You and change your search method to add these two fields:
def self.search(search_project, search_client)
return scoped unless search_project.present? || search_client.present?
where(['project_name LIKE ? AND client LIKE ?', "%#{search_project}%", "%#{search_client}%"])
end
But please note the || will return scope if your search_project OR search_client are not present, you can change for AND (&&) if you prefer.
Also, the AND will return only if both match, I mean the combination of search... You can also change it to OR if you want.
Having the search form:
Your search form:
<%= form_tag search_index_path, method: :get do %>
<%= text_field_tag :project, params[:project] %>
<%= text_field_tag :client, params[:client] %>
<%= submit_tag "Search", name: nil %>
<% end %>
Then your controller must send the combination to the model:
#project_search = Project.search(params[:project], params[:client]).all
I think it will solve the problem...
I've been using MetaSearch in my application and found it quite convenient. If you've already considered it, what problems did you have?
There's also Ransack by the same author, it's a successor to MetaSearch.
A simple explanation can be found in this rails cast
Basically, we have to test if the params contain a specific field and create the filter. See the example below:
def find_products
products = Product.order(:name)
products = products.where("name like ?", "%#{keywords}%") if keywords.present?
products = products.where(category_id: category_id) if category_id.present?
products = products.where("price >= ?", min_price) if min_price.present?
products = products.where("price <= ?", max_price) if max_price.present?
products
end
An alternative is Ransack. Ransack enables the creation of both simple and advanced search forms for your Ruby on Rails application