Search code examples
ruby-on-railsrubyruby-on-rails-3ruby-on-rails-3.1nested-sets

How does the Nested Set Gem work, and how can I incorporate it into my project?


I have recently been advised that for my current rails app relationships I should use the gem nested set. ( My previous thread / question here) I currently have 3 models,

Categories has_many Subcategories
Subcategories belongs_to Categories, and has_many products.
Product belongs_to Subcategory. I wanted to display it something like this

+Category
----Subcategory
--------Product
--------Product
----Subcategory
--------Product
--------Product

+Category
----Subcategory
--------Product
--------Product

So if I were to do this in nested_set, how would I set this up in my Models? Would I remove my subcategory and product models, and just add acts_as_nested_set in the Category model? and once I have the model taken care of, what will I update my controllers actions with, to be able to create nodes in the nested set I create?

I guess just help me understand how I can do the CRUD, create, read, update, and destroying of this nested_set list.

Here is some code I have already

Categories Controller:

class CategoriesController < ApplicationController
def new
  @category = Category.new
  @count = Category.count
end

def create
@category = Category.new(params[:category])
if @category.save
  redirect_to products_path, :notice => "Category created! Woo Hoo!"
else
  render "new"
end
end

def edit
  @category = Category.find(params[:id]) 
end

def destroy
  @category = Category.find(params[:id])
  @category.destroy
  flash[:notice] = "Category has been obliterated!"
  redirect_to products_path
end

def update
  @category = Category.find(params[:id])

if @category.update_attributes(params[:category])
  flash[:notice] = "Changed it for ya!"
  redirect_to products_path
else 
  flash[:alert] = "Category has not been updated."
  render :action => "edit"
end
end

def show
  @category = Category.find(params[:id])
end

def index
  @categories = Category.all
end 
end

Category Model:

class Category < ActiveRecord::Base
  acts_as_nested_set
  has_many :subcategories
  validates_uniqueness_of :position
  scope :position, order("position asc")

end

Subcategory Model:

class Subcategory < ActiveRecord::Base
  belongs_to :category
  has_many :products
  scope :position, order("position asc")
end

And finally, Product Model:

class Product < ActiveRecord::Base
  belongs_to :subcategory
  has_many :products
  scope :position, order("position asc")
end

Any help would be very appreciated.


Solution

  • I would go with a Category and a Product like so:

    class Product > ActiveRecord::Base
      belongs_to :category
    end
    
    class Category > ActiveRecord::Base
      has_many :products
      acts_as_nested_set
    end
    
    class CategoryController < ApplicationController
       def create
    
          @category = params[:id] ? Category.find(params[:id]).children.new(params[:category]) : Category.new(params[:category])
    
          if @category.save
              redirect_to products_path, :notice => "Category created! Woo Hoo!"
          else
              render "new" 
          end
       end
    
       def new
          @category = params[:id] ? Category.find(params[:id]).children.new : Category.new
       end
    
       def index
          @categories = params[:id] ? Category.find(params[:id]).children : Category.all
       end
    end
    
    #config/routes.rb your categories resource could be something like..
    resources :categories do
       resources :children, :controller => :categories, 
                                  :only => [:index, :new, :create]
    end
    

    this way is the most flexible, as you can put your products in any a category at any level.