简体   繁体   中英

Best way to define global objects in Ruby on Rails

I have an app that has two models Category and Product

Products belong to categories.

I have created a navbar dropdown that requires an @category and @product object to be available across the entire app (the navbar is shown on every page of the application.)

What I can't work out is the best place to put these basic definitions without defining them multiple times in every page definition.

I need to define the following:

@category = Category.all

@products = @category.products.all

The navbar loop will then look something like this.

<% @category.each do |c| %>
  <%= c.name %>
  <% @products.each do |p| %>
    <% link_to product_path(p) do %>
      <%= p.name %>
    <% end %>
  <% end %>
<% end %>

I am a bit of a rails newbie so I am sure there are some errors in here but any help would be much appreciated!

If you need them in every single page of app, you can set them in ApplicationController 's before_filter :

class ApplicationController
  before_filter :get_categories

  # ...
  private
  def get_categories
    @categories = Category.includes(:products)
  end
end

then, you can write in your view:

<% @categories.each do |category| %>
  <%= category.name %>
  <% category.products.each do |product| %>
    <%= link_to p.name, p %>
  <% end %>
<% end %>

I also fixed some other errors and convention incompatibilities.

The following code is incorrect.

@category = Category.all
@products = @category.products.all

This code assigns to @categories all the categories, then it attempts to fetch the products. It will not work, unless you have defined a products class method in the Category model. But I don't think so, otherwise you will just have to call Product.all .

Moreover, in the code below, you are trying to display the list of products per category, which definitely don't work with the two assignments before. According to what you are trying to achieve, you can't pre-assign the @products , because you want the products for a specific category.

Let's inline everything into the code.

<% Category.all.each do |category| %>
  <%= category.name %>
  <% category.products.each do |product| %>
    <%= link_to product_path(product) do %>
      <%= product.name %>
    <% end %>
  <% end %>
<% end %>

Next step is to make the code a little bit more performant, giving you need it everywhere.

<% Category.select(:id, :name).each do |category| %>
  <%= category.name %>
  <% category.products.select(:id, :name).each do |product| %>
    <%= link_to product_path(product) do %>
      <%= product.name %>
    <% end %>
  <% end %>
<% end %>

You could use pluck , but it will return an array and it will require a little bit more manipulation. However, it's way more performant.

<% Category.pluck(:id, :name).each do |category_id, category_name| %>
  <%= category_name %>
  <% Product.where(category_id: category_id).pluck(:id, :name).each do |product_id, product_name| %>
    <%= link_to product_name, product_path(id: product_id) %>
  <% end %>
<% end %>

It's not a good idea to chain all those methods inside a view, let's extract some code into the model.

class Category
  def self.simple_listing
    order(:name).pluck(:id, :name)
  end
end

class Product
  def self.simple_category_listing(category_id)
    where(category_id: category_id).order(:name).pluck(:id, :name)
  end
end

<% Category.simple_listing.each do |category_id, category_name| %>
  <%= category_name %>
  <% Product.simple_category_listing(category_id).each do |product_id, product_name| %>
    <%= link_to product_name, product_path(id: product_id) %>
  <% end %>
<% end %>

You can leave all this code into the view, or extract it into a partial. You don't even need to add a controller before filter, or make it "global". The code is self-contained, does not pollute the name space with instance variables, and it can easily be placed whenever you need it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM