All threads / Is there a better way to simplify this?

Ask A Question

Notifications

You’re not receiving notifications from this thread.

Is there a better way to simplify this?

Alan Reid asked in Rails

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.

Wouldn't there be an includes(:product) somewhere to avoid too many request to the DB?

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 %>

does it have to be "Products_count"?

You can customize it, but that's the inferred default name.

Join the discussion

Want to stay up-to-date with Ruby on Rails?

Join 35,699+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.

    logo Created with Sketch.

    Ruby on Rails tutorials, guides, and screencasts for web developers learning Ruby, Rails, Javascript, Turbolinks, Stimulus.js, Vue.js, and more. Icons by Icons8

    © 2020 GoRails, LLC. All rights reserved.