sort by model attribute in controller in user show - ruby-on-rails

I have a book review/discussion site and would like to order all the users books on their profile page based on wether they are currently reading that book. I added a column to the book model called current and when users add a book they can check a box that says "I'm currently reading this." If the user clicks the box then the book object has a :current attribute with a value of "1". If they don't click the box then the value of :current is "0".
In my view I have this code to tell the user which books they are currently reading, and it works fine.
<% if book.current == "1" %>
<h4>I am currently reading this book</h4>
<% end %>
However in my show user controller I can't make it order the incomiing objects by the :current status.
Here's what I've tried:
def show
#user = User.find_by_id(params[:id])
#books= Book.all.sort { |p1, p2| p1.current <=> p2.current }
end
and
def show
#user = User.find_by_id(params[:id])
#books = Book.all
#books = #books.order(:current)
end
and
def show
#user = User.find_by_id(params[:id])
#books = Book.all
#books = #books.order(current: :desc)
end
and, just to make sure it was going the right way
def show
#user = User.find_by_id(params[:id])
#books = Book.all
#books = #books.order(current: :asc)
end
None work. And, none throw errors, they just don't sort it. The "current" book that is last instead of first. Here's the code that displays and loops out the users books. This code also works fine.
<% #user.books.each do |book| %>
<% if params[:id].to_s == book.user_id.to_s %>
<ul class="profileDisplay" >
<img alt="Book Jacket" class="homepageImages" src=<%= book.image %> >
<% if book.current == "1" %>
<h4>I am currently reading this book</h4>
<% end %>
<p><b>Contributor: </b><%= book.user.name %></p>
<p><b>Title: </b><%= book.title %></p>
<p><b>Author: </b><%= book.author %></p>
<p><b>Genre: </b><%= book.genre %></p>
<p><b>Publication Date: </b><%= book.publication_date %></p>
<p><b>Publisher: </b><%= book.publisher %></p>
<p><b>Synopsis: </b><%= book.synopsis %></p>
<% if params[:id] == session[:user_id].to_s || params[:action] == "profile" %>
<%= button_to "Edit Book", book_edit_path(book.id), method: "get", class: "btn btn-primary col-xs-2" %>
<%= button_to "Delete Book", delete_book_path(book.id), method: :delete, data: {confirm: "Are you sure you want to delete this book and all it's reviews?"}, class: "btn btn-danger col-xs-2" %>
<% end %>
</ul>
<% end %>
<% end %>

On view, #user.books are not sorted yet.
You sorted them and assigned it into #books
I think it should be like this
CONTROLLER
def show
#user = User.find_by_id(params[:id])
#books = #user.books.order(current: :desc)
end
VIEW
<% #books.each do |book| %>
<!-- show book records here -->
<% end %>

add default scope in book model
default_scope { order(:current=> :desc) }
and your controller
#user = User.find_by_id(params[:id])
and views
<% #user.books.each do |book| %>
#do whatever
<% end %>

Related

How to render search results only when the user press search

Does anyone know how to only display search results once you have clicked the search button?
At the moment, my page is currently displaying everything from my #flights.each. But I only want this information to become visible once they have clicked search :)
my index.html.erb
<h1>Flights#index</h1>
<p>Find me in app/views/flights/index.html.erb</p>
From Airport:
<%= form_for(flights_index_path, method: :get) do %>
<%= text_field_tag :from_airport_id, params[:from_airport_id] %>
<%= submit_tag 'Search' %>
<% end %>
<% #flights.each do |f| %>
<br>
<br>
Flight from <%= f.from_airport_id %>
Arriving at <%= f.to_airport_id %>
<% end %>
my controller
class FlightsController < ApplicationController
def index
if params[:from_airport_id]
#flights = Flight.where('from_airport_id LIKE ?', "%#{params[:from_airport_id]}%")
else
#flights = Flight.all
end
end
private
def flights_path
params.require(:flight).permit(:flight, :from_airport_id)
end
end
I believe it's because you have #flights = Flight.all in the else condition. When there is no query(such as after hitting the submit button and passing over the params) it will default to show all the flights. I'd take this line out and only have
def index
if params[:from_airport_id]
#flights = Flight.where('from_airport_id LIKE ?', "%#{params[:from_airport_id]}%")
else
#flights = []
end
end
Or you can look to have an AJAX request from your flights search and render the form that way.

How do I initiate my each loop only after I have click my submit button in Rails?

I have a Rails app that searches through a DB and returns items. At the moment in my view it automatically returns the default search results ("") without me needing to hit my submit_tag. How do I go about only making this action happen once I have hit the submit_tag? Any help would be greatly appreciated!
Here is my view:
<%= form_tag(new_design_path, method: :get) do %>
<%= label_tag(:q, "Search all designs:") %>
<%= text_field_tag(:q) %>
<%= submit_tag("Search") %>
<% end %><br>
<!-- Button to return one random design -->
<%= form_tag(new_design_path, method: :get) do %>
<%= label_tag(:q, "Inspire me! Click here for a random design:") %>
<%= submit_tag("Random Design") %>
<% end %>
<h2>Search results:</h2>
<% #random.each do |design| %>
<h3><%= design['name'] %></h3>
<h5><%= image_tag design['thumbnail_url'] %></h5>
<% end %>
<% #search.each do |design| %>
<div class="design">
<h3 class="design_name"><%= design['name'] %></h3>
<h5><%= image_tag design['thumbnail_url'] %></h5>
<%= button_to 'Save to Favourites',
designs_path(
design: design.slice('name', 'thumbnail_url')
),method: :post %>
</div>
<% end %>
And my controller:
class DesignsController < ApplicationController
def index
#designs = Design.all.order("created_at DESC")
end
def new
# returns an array of hashes
#search = SpoonflowerApi.new.find(params[:q])['results']
#random = SpoonflowerApi.new.random(rand(1..740579), 1)['results']
end
def create
#design = Design.new(design_params)
#design.save
if #design.save
flash[:notice] = 'Design has been added to favourites!'
else
flash[:notice] = 'Design already in Favourites!'
end
redirect_to new_design_path
end
def destroy
#design = Design.find(params[:id])
#design.destroy
flash[:notice] = 'Design removed from favourites!'
redirect_to designs_path
end
private
def design_params
params.require(:design).permit(:name, :thumbnail_url)
end
end
new is used to populate the initial form, so if you don't want anything for those fields you should just set both #search and #random to an empty array in new. You don't show any code for your model, so it's not really clear what Api is.
show should be called once you submit the form
def new
#search = []
#random = []
end
then move the logic to provide the search results or random record into the show method
def show
# not sure what you want to do here
# since it seems like you have 2 buttons you need logic to provide data
# based on the button
# maybe something like this
if params[:q].nil?
#search = []
#random = Api.new.random(rand(1..740579), 1)['results']
else
#search = Api.new.find(params[:q])['results']
#random = []
end
end
If I understood you correctly, your view showing some search results before you click the search button.
Since you directly send the params[:q] to SpoonflowerApi, I am guessing that it returns some default value and your view draw it.
Simply update your controller to:
def new
#search=[]
#search = SpoonflowerApi.new.find(params[:q])['results'] unless params[:q].nil?
#random = SpoonflowerApi.new.random(rand(1..740579), 1)['results']
end

Using search function in rails to retrieve a result and display it first in the index page

I am trying to implement search functionality in my rails app where I search and display a particular search result first on my index.html.erb view. At the moment I have a search function working and it returns the particular item on its own on the index page.
Ideally I would like to have this item displayed first and then all the other items to display below.
My code is as follows:
brand.rb
def self.search(query)
where("author like ?", "%#{query}%")
end
brand_controller.rb
def index
if params[:search]
#brand = Brand.search(params[:search]).order("created_at DESC")
else
#brand = Brand.all.order(':date')
end
end
I know the where method returns the value as an array so I could probably use array.first to output this result first but is there an easier way to output my desired view. Thanks!
So turned out to be a pretty simple solution, I blame mondays.
All I had to do was create another variable for my search and iterate that result first, then iterate through the rest of the items.
in my controller
def index
#brand = Brand.order('created_at DESC')
if params[:search]
#brand = Brand.search(params[:search]).order("author DESC")
#other = Brand.search_all(params[:search]).order("author DESC")
else
#brand = Brand.all.order('author DESC')
end
end
In my model
def self.search(query)
where("author like ?", "%#{query}%")
end
def self.search_all(query)
where("author not like ?", "%#{query}%")
end
and finally in my view
<% if #brand.any? %>
<% #brand.in_groups_of(2) do |group| %>
<% group.each do |brand| %>
<% if brand %>
<h4> <%= brand.author %></h4>
<a href="<%=brand_path(brand)%>">
<%=image_tag brand.brand_logo, class: 'img-rounded', :"data-uid" => brand.uid %> </a>
<% end %>
<% end %>
<% end %>
<% end %>
<% if #other %>
<% #other.in_groups_of(2) do |group| %>
<% group.each do |other| %>
<% if other %>
<h4> <%= other.author%></h4>
<a href="<%=brand_path(other)%>">
<%=image_tag other.brand_logo %> </a>
<% end %>
<% end %>
<% end %>
<% end %>

Why does my search show all results with searching?

I setup a search on my Products index page with PgSearch and Will-Paginate like this:
ProductsController
def index
#products = Product.text_search(params[:query]).page(params[:page]).per_page(5)
end
Products Model
include PgSearch
pg_search_scope :search,
def self.text_search(query)
if query.present?
search(query)
else
scoped
end
end
Product index page
<%= form_tag products_path, method: :get do %>
<%= text_field_tag :query, params[:query] %>
<%= submit_tag "Search", name: nil %>
<% end %>
<% if #products.blank? %>
No Results
<% else %>
<% #products.each do |product| %>
<%= product.name %>
<% end %>
<% end %>
But the problem I'm having now is that when I go to the Product index page, it shows all of the products when I want it to show nothing until a search is done. If the search is blank, return No Results but when you first hit the page it should show nothing. How would this be done?
You probably want to only run a text_search when a search parameter is present. You can put this logic into the view, the controller, or in the model.
In the view
<% if params[:query].present? %>
<% if #products.blank? %>
No Results
<% else %>
<% #products.each do |product| %>
<%= product.name %>
<% end %>
<% end %>
<% end %>
In the controller
def index
if params[:query].present?
#products = Product.text_search(params[:query]).page(params[:page]).per_page(5)
else
#products = Product.none # NOTE: Rails 4 only
end
end
In the model
# create a new method to encapsulate this search logic then use it in the controller
def self.search(value)
if value.present?
Product.text_search(value)
else
Product.none # NOTE: Rails 4 only
end
end
The old saying goes "fat model, skinny controller" so you might want to opt for the model method which will keep your controller and views simpler.
Put your display logic inside an if statement:
<% if params[:query].present? %>
<% if #products.blank? %>
No Results
<% else %>
<% #products.each do |product| %>
<%= product.name %>
<% end %>
<% end %>
<% end %>
Although I'm not familiar with how pg search works, you could do something like this in your method.
It's a nice refactoring as well as it avoids checking for existence and making decisions on params (code smell)
def self.text_search(query = "")
search(query)
end
As I said, not sure how pg_search works. Maybe when you browse for nothing, it returns all records. If that's the case, you can just have it return an empty array. Something like this would do
def self.text_search(query)
return [] if query.nil?
search(query)
end

Cannot reverse order because of pagination

I want to order users by descending. However, when I add .reverse it reverses the users a page at a time, not all together.
show_followers:
<%= render #users.reverse %>
<%= will_paginate %>
Users/_user:
<%= link_to user.name, user %>
<% unless #user.nil? %>
<% if request.path == "/users/#{#user.id}/listening_to" %>
<br/>
<span id="member_since"><%= "Listening Since" %> <%= #user.relationships.find_by_followed_id(user.id).created_at.strftime("%b. %d, %Y") %></span>
<% end %>
<% if request.path == "/users/#{#user.id}/listeners" %>
<br/>
<span id="member_since"><%= "Listener Since" %> <%= user.relationships.find_by_followed_id(#user.id).created_at.strftime("%b. %d, %Y") %></span>
<% end %>
<% end %>
Controller:
#users = #user.followed_users.paginate(page: params[:page])
It seems Rails intelligently orders by the relationships_create like I wanted but it's in ascending order. I want the most recent follower to show up on the top of the list.
In the controller action, you probably have some code that looks something like this: #users = User.paginate(:page => params[:page]).
You need to put the reverse in that statement, so it looks like this: #users = User.paginate(:page => params[:page]).order('id DESC') (perhaps with id replaced by created_at).
#users = #user.followed_users.reverse.paginate(page: params[:page])
Reverse at the controller level prior to pagination

Resources