Ruby on Rails: Grouping search results by category - ruby-on-rails

I am working on a RoR WebApp. I'm trying to group results on the search page based on their taxonomy. What I want to do is to show a header for a category and list all results under that category. Something like:
CAT 1
products
CAT2
products
CAT3
.
.
I am trying using the following code:
<% if products.any? %> #products is the list of search results
<%= render :partial=> 'product_listing_feature', :locals => {:scope => scope, :scope_type => scope_type} %>
<div id="ql_product"></div>
<div class="product_rows">
<%taxons.each do |taxon|%> # taxons contains the list of unique categories in products
<div class = "product_row">
<h1><%=taxon%></h1>
<% taxonProducts = Array.new %>
<% products.each do |product| %>
<%#ptaxon = product.get_taxonomy%>
<%if #ptaxon == taxon%>
<% taxonProducts.push(product) %>
<% end %>
<% end %>
<div class ="featured_product_list">
<ul class = "featured_products">
<div class = "page">
<%= render :partial=> 'product_listing', :locals=>{:collection=> taxonProducts} %>
</div>
</ul>
</div>
</div>
<% end %>
</div>
<% end %>
Surprisingly it starts the 2nd category from a new row, but the following categories appeared jumbled up, something like
CAT1
products
CAT2
products CAT3
products
picture would give a better idea.
I am really surprised why it works only for one iteration. Could someone please help me fix this.
Thanks a lot

Way, way too much logic for a view. Just use group_by in your controller, which will give you a mapping of names to arrays of products:
products = Product.includes(:taxon).group_by { |p| p.taxon.name }

Related

Rails retrieving all records in show controller

In my rails category show controller for categories I have it setup like this
def show
#categories = Category.find_by(params[:name])
end
But when I visit this controller it returns all records of products found in the category instead of single category.
Here is the code in my view controller for category
<div class="grid">
<% #categories.products.each do |product| %>
<%= link_to product_path(id: product.slug, category_name: product.category.name), class: "card" do %>
<div class="product-image">
<%= image_tag product.productpic.url if product.productpic? %>
</div>
<div class="product-text">
<h2 class="product-title"> <%= product.name %></h2>
<h3 class="product-price">£<%= product.price %></h3>
</div>
<% end %>
<% end %>
</div>
What am i doing wrong here?
First of all, for security purposes, you should never trust the params hash to retrieve records. Rails will "make the data safe" if you use a hash as your arguments. Use this code below:
def show
#category = Category.find_by(name: params[:name])
end
Second, usually on a show page, you only want to retrieve one record and therefore the variable should be named as singular. I corrected that above.
Third, it helps if you use proper indenting when posting examples. It makes it easier for us to help you.
Fourth, the line below (I changed #categories to #category) is basically saying: "Now that I have this single category, find all the products associated with it in the products table and put them into the |product| variable for iteration"
<% #category.products.each do |product| %>
I'm not sure what you want to do with the category, but if you keep this line of code, it will always show you all the products. Maybe you only want to show the most recent 3, in which case you could do something like this:
In your controller:
def show
#category = Category.find_by(name: params[:name])
#recent_products = #category.products.order(created_at: :desc).limit(3)
end
In your view:
<div class="grid">
<% #recent_products.each do |product| %>
<%= link_to product_path(id: product.slug, category_name: product.category.name), class: "card" do %>
<div class="product-image">
<%= image_tag product.productpic.url if product.productpic? %>
</div>
<div class="product-text">
<h2 class="product-title"> <%= product.name %></h2>
<h3 class="product-price">£<%= product.price %></h3>
</div>
<% end %>
<% end %>
</div>
You can do this way
in your controller you can write this code
def show
#category = Category.find_by_name(params[:name])
end
and in your view it will work
<div class="grid">
<% #category.products.each do |product|%>
// place your code what you want to display
<% end %>
</div>
I hope it would help you and still if you have any concern please let me know.

View loop based on model column data [Rails]

This is probably very simple to do, but I am having trouble wrapping my head around it. I have a model called "document" and a field within the model called: category.
Category can consist of 3 options:
Meeting Notes
Sales
Engineering
In my view I have: [index.html.erb]
<% #documents.each do |document| %>
<div class="table-div">
<div class="col-sm-12 col-md-12 div-table-label">
<strong><%= document.category %></strong>
</div>
<div class="col-sm-12 col-md-12">
<%= link_to document.title, document %>
</div>
</div>
<% end %>
In my controller, just the default:
def index
#documents = Document.all
end
What I want to do is loop through each category and grab all documents tied to that category to group them together.
Right now, it just prints each document with its category heading in the order it is created, no logic to it at all.
Thanks.
Try
def index
#documents = Document.where(true).order(:category).order(:title)
end
so that the ordering is done on the database which can be faster especially if you have a index on the category column and to order the documents by title
And change the view to render the header when ever a new category is encountered
<% category = "" %>
<% #documents.each do |document| %>
<div class="table-div">
<% if category != document.category %>
<div class="col-sm-12 col-md-12 div-table-label">
<strong><%= document.category %></strong>
</div>
<% category = document.category %>
<% end %>
<div class="col-sm-12 col-md-12">
<%= link_to document.title, document %>
</div>
</div>
<% end %>
What I want to do is loop through each category and grab all documents
tied to that category to group them together.
I'd start with an easy way to find documents by category name. For this, a scope is great:
class Document
# ...
scope :for_category, -> category_name { where(category: category_name) }
# ...
end
Then you could loop through the category names and find all documents for that category, such as:
ALL_CATEGORIES = ["Meeting Notes", "Sales", "Engineering"]
All_CATEGORIES.each do |category|
documents = Document.for_category(category)
# Do something with the documents for this category...
end
Of course, it's not really ideal to put this much logic into the view, but expanding on this is a bit beyond the scope of this question. If curious, look into Avdi Grimm's Exhibit Pattern.
You could always create a static method in the Document class that does the following:
class Document
...
def self.all_by_category
docs_by_category = {}
all.each do |document|
if docs_by_category.has_key? document.category
docs_by_category[document.category] << document
else
docs_by_category[document.category] = [document]
end
end
docs_by_category
end
You could then look through the returned hash and print out the documents that are a part of each category like so:
def index
#you may want to change the name because it's misleading to leave it as #documents
#documents_by_category = Document.all_by_cateogry # returns hash
end
and then
<% #documents_by_category.each do |category_name, documents|
documents.each do |document| %>
YOUR CODE HERE
<% end %>
<% end %>
If the number of documents are high, however, this method may become inefficient.
It might be better to create another database table for categories, and then use a Category model to query each category and get their associated documents.
do you mean something like:
#categories.each do |cat|
# output cat.name or something
cat.documents.each do |doc|
# doc.name or whatever
end
end
you'd obviously need to get the #categories in your controller
def index
#categories = Category.all
end
alternatively you could be getting the documents grouped by their category
def index
#documents = Document.all.group_by(&:category)
end
see here

combining 2 tables, looping through the results and displaying items from each

So I'm trying to combine two tables and show the results in order of the start_date.
I've tried a few things but because its technically a nested loop its giving me double results for each item.
The code i currently have is as follows
<% #subcategory = Subcategory.all %>
<% #product = Product.all %>
<% (#product + #subcategory).each do |product, subcategory|%>
<% if product.display_on_home_page and !product.is_highlight_product and !(product == '..') or subcategory.
display_on_home_page and !subcategory.is_highlight_product and !(subcategory == '..')%>
<div class="column_entry">
<%= link_to image_tag(subcategory.image_attachment.url(:normal_page_size)), subcategories_content_url(subcategory.id), :controller=>'subcategories' %>
</div>
<% end %>
<% if product.price_from %>
<div class="column_entry">
<div class="product_special">
<span class="a">From Only</span>
<span class="b"><%= number_to_currency(product.price,:unit=>'€') %></span>
</div>
<%= link_to image_tag(product.product_image.url(:normal_page_size)), products_content_url(product.id), :controller=>'products' %>
</div>
<% else %>
<div class="column_entry">
<div class="product_special">
<span class="a">Only</span>
<span class="b"><%= number_to_currency(product.price,:unit=>'€') %></span>
</div>
<%= link_to image_tag(product.product_image.url(:normal_page_size)), products_content_url(product.id), :controller=>'products' %>
</div>
<% end %>
<% end %>
I know this is quite a long an complex statement, its supposed to loop through all of the subcategories and all of the products and display the images, there are also two different ways of displaying the price based on a boolean that says whether the price is a specific amount or it starts from a given price.
at the moment its reading through the loop but its giving me the error
undefined method `is_highlight_product' for nil:NilClass
since this is the first column in the table that is referenced and its breaking here I think that there must be some conflict in its ability to see the information stored in the table.
I'm still quite new to ruby on rails so any help or even just a nudge in the right direction would be very much appreciated.
If you would like more information just ask in the comments and I'll put it up as fast as I can.
The problem here is, when you do something like this:
(#product + #subcategory).each do |product, subcategory|
The local variable product will iterate firstly through products, then through subcategories, and the local variable subcategory will always be nil.
What you can do, a dirty way - check
if product.is_a?(Product)
# do your things
elsif product.is_a?(Subcategory)
# do other things
end

RoR View: List results grouped under categories

I am trying to group a set of products (obtained typing a query) based on taxonomy(on of the attributes of a product)
My desired output is
Taxonomy 1
prod1 prod2 prod3 prod4
prod 5 ...
Taxonomy 2
pod6 prod7
Taxonomy 3
prod8 prod9..
I am using the following code in the view:
<% taxonomies.each do |taxonomy|%> #"taxomonies" is a set of unique taxonomies for retrieved products
<h1><%= taxonomy%></h1>
<ul>
<% collection.each_with_index do |product,i| %> #"collection" is the list of products retrieved
<li>
<%#ptaxon = product.get_taxonomy%>
<%if #ptaxon == taxonomy%>
<%code for listing product%>
<%end%>
</li>
<%end%>
</ul>
<%end%>
This groups the products based on taxonomies but the format is not what I desire. Could someone please point out my mistake.
EDIT: also tried using < br > , but doesn't help!
This is the output I'm getting. I want the taxonomies earrings, bracelets and necklaces to start from a new line.
Thanks
If you have your associations setup correctly, you can do it like this:
<% taxonomies.each do |taxonomy| %>
<%= taxonomy.name %>
<% taxonomy.products.each do |product| %>
<%= product.name %>
<% end %>
<% end %>
Models should be something like:
class Taxonomy
has_many :products
end
class Product
belongs_to :taxonomy
end

rails where condition works in console, but not in web server

I am new in RoR.
The problem is, I created fully functional product categorization with Ancesrty. But now I want to be able to retrieve products that is under these subcategories.
This is my categories show controller
#category = Category.find(params[:id])
Here is categories#show view.
<b>Name of the category:</b>
<%= #category.name %>
<div class="product"
</div>
</p>
<% unless #category.children.empty? %>
<ul id="sub-menu">
<% #category.children.each do |sub1| %>
<%= link_to (sub1.name), sub1 %>
<%end%>
<%end%>
It all works fine. but now I want to add in view categories/show function that shows all products that is under that category.
I added such code.
In category/show controller
#cat_id = #category.id
#product = Product.where("category_id = ?",#cat_id)
In the categories show view I added
<td><%= #product.name %></td>
Then clicking on some subcategory where should appear few products, there just shows up Product
To check if the code is right I put in the console. There it works fine and retrieve products related to this category.
I dont understand why then code not working in webserver when I launch application ?
Could it be because of some erorr in Associations ?
Thanks !
in your controller, a more readable way is to use the plural form to indicate that you are expecting more than 1 object
#products = Product.where("category_id = ?", #cat_id)
Then in the view, just loop through these products
<% #products.each do |product| %>
<%= product.name %>
<% end %>
#product = Product.where("category_id = ?",#cat_id)
will return an array if there are any products. So you will need to loop through the array.
<% #product.each do |product| %>
<%= product.name %>
<% end %>
I accept both of the answers, But I want to suggest to use Active Record Association for this type of problems. This makes your solution easier.
If you want to fetch only one product, you can use the find_by_ helper method of the model:
#product = Product.find_by_category_id(#cat_id)
With this it will fetch the first matching product which has category_id equal to #cat_id.
If you want to fetch all the products which belong to a category, you need to fetch all the products as others suggested:
#products = Product.where(:category_id => #cat_id)
And then in the view:
<% #products.each do |product| %>
<%= product.name %>
<% end -%>

Resources