2

I have nested routes of categories/subcategories/products and my controller and view files are setup accordingly, but I now have some products that don't have subcategories. How, if possible, can I have some products part of a category, while others are part of a sub-category? I've seen many other posts on this, but none seem to address exactly what I'm looking to do.

current nested routes

resources :categories do
  resources :subcategories do
    resources :products
  end
end

additional needed nested routes

resources :categories do
  resources :products
end

My current Products controller Create method

def create
    @category = Category.friendly.find(params[:category_id])
    @subcategory = Subcategory.friendly.find(params[:subcategory_id])
    @product = Product.new(product_params)

    respond_to do |format|
      if @product.save
        format.html { redirect_to category_subcategory_product_path(@category, @subcategory, @product), notice: 'Product was successfully created.' }
        format.json { render :show, status: :created, location: category_subcategory_product_path(@category, @subcategory, @product) }
      else
        ...
      end
    end
  end

models

class Category < ApplicationRecord
  has_many :subcategories
  has_many :products, through: :subcategories
end

class Subcategory < ApplicationRecord
  has_many :products
  belongs_to :category
end

class Product < ApplicationRecord
  belongs_to :subcategory
end
Charles Smith
  • 3,201
  • 4
  • 36
  • 80

1 Answers1

3

What I would do here is remove the Subcategory model and let Categories belong to themselves. This would let you create a nesting hierarchy of categories (which would allow you to get even more granular, if you wanted to).

class Category
  has_many :categories
  belongs_to :category
  has_many :products
end

class Product
  belongs_to :category
end

Any "top level" categories would have a category_id of nil, and any subcategories would belong_to an existing one.

top_level = Category.create(slug: "top_level", category_id: nil)
subcategory = Category.create(slug: "subcategory_1", category_id: top_level.id)
Product.create(category: top_level)
Product.create(category: subcategory)

In your routes, you can make something like this:

get "/:category/products", to: "products#index"
get "/:category/:subcategory/products", to: "products#index"
brainbag
  • 1,007
  • 9
  • 23
  • 1
    this is awesome. Should I add the new routes to my existing nested routes or replace altogether? – Charles Smith Nov 23 '17 at 01:25
  • It depends on your app's design, I think. If you're just using categories to filter products, I would use the routes I listed, along with `resources :products` for your main products resource routes if you need it. – brainbag Nov 23 '17 at 02:04