Is there a better way to simplify this?
Hi again,
I want to know if there is a better way to get a list of categories which only have products.
I have tried to do @categories = Category.where(category_type: 'product').order(name: :asc).products.any
but that seems to error and i am not sure why.
My Category
has_many :products
and my product
belongs_to :category
so i assumed this would work.
In my controller i am am getting a list of categories.
def index
@categories = Category.where(category_type: 'product').order(name: :asc)
end
Then populating them on the page like as per below... now this work but its dirty and ends up with loads of calls to the DB which i don't personally like haha.
<% @categories.each do |category| %>
<% if category.products.any? %>
<div class="pad1 list-row">
<div class="space-left4 pad1x row-details">
<!-- Details -->
<div class="details-name"><%= category.name %></div>
<div class="details-sub">Products: <%= category.products.count %></div>
</div>
<div class="row-actions"></div>
</div>
<% end %>
<% end %>
That's a typo on here where I typed it out. In the code it says .products.any
. Updated the post, thanks for spotting
One thing you could do is to set up a counter cache on Category so that you can store the number of products on it. Then when you query you can query for Category.where("products_count > 0")
to improve your query and remove that if statement in the view.
Yup, once you've gathered your categories that have products efficiently, you can preload them with includes
as well.
So i would set up the counter cache like so?
belongs_to :category counter_cache: true
The i could use the Category.where("products_count > 0")
query. The i assume with those results i could get the products using 'includes(:product)' like we did before?
For the most part, yes. You also need to add a products_count
integer column to categories, default it to 0, and then update the records in the database to have the correct amount. Then your counter_cache will update that column and your query of Category.where("products_count > 0")
will work.
Your view just references the products count, so you can actually print out the new column value without loading any products making it even more efficient.
<% @categories.each do |category| %>
<div class="pad1 list-row">
<div class="space-left4 pad1x row-details">
<div class="details-name"><%= category.name %></div>
<div class="details-sub">Products: <%= category.products_count %></div>
</div>
<div class="row-actions"></div>
</div>
<% end %>