Rails: categories and sub-categories model rails
Create a model that has references to itself for a sub-category (or a sub-sub-category, etc):
class Category < ActiveRecord::Base
has_many :subcategories, :class_name => "Category", :foreign_key => "parent_id", :dependent => :destroy
belongs_to :parent_category, :class_name => "Category", :optional => true
end
- the
has_many
defines asubcategories
association of the model typeCategory
. Ie it uses the same table. - the
belongs_to
defines a relation back to the parent category, accessible via@category.parent_category
For more information on model associations, has_many
or belongs_to
, read the Associations Basics Guide.
To create the table use this migration:
class CreateCategories < ActiveRecord::Migration
def self.up
create_table :category do |t|
t.string :text
# table name should be in plural
t.references :parent_category, foreign_key: { to_table: :categories }
t.timestamps
end
end
end
Note: this table format is (slightly) different than you proposed, but I suppose that this is not a real problem.
The Migrations Guide contains more information on database migrations.
In your controller use
def index
@category = nil
@categories = Category.find(:all, :conditions => {:parent_id => nil } )
end
to find all categories without a parent, ie the main categories
To find all sub-categories of any given category use:
# Show subcategory
def show
# Find the category belonging to the given id
@category = Category.find(params[:id])
# Grab all sub-categories
@categories = @category.parent_category
# We want to reuse the index renderer:
render :action => :index
end
To add a new category use:
def new
@category = Category.new
@category.parent_category = Category.find(params[:id]) unless params[:id].nil?
end
It creates a new category and sets the parent category, if it is provided (otherwise it becomes a Main Category)
Note: I used the old rails syntax (due to laziness), but for modern versions of Rails the principle is the same.
In your categories/index.html.erb
you can use something like this:
<h1><%= @category.nil? ? 'Main categories' : category.text %></h1>
<table>
<% @categories.each do |category| %>
<tr>
<td><%= link_to category.text, category_path(category) %></td>
<td><%= link_to 'Edit', edit_category_path(category) unless category.parent.nil? %></td>
<td><%= link_to 'Destroy', category_path(category), :confirm => 'Are you sure?', :method => :delete unless category.parent.nil? %></td>
</tr>
<% end %>
</table>
<p>
<%= link_to 'Back', @category.parent_category.nil? ? categories_path : category_path(@category.parent_category) unless @category.nil? %>
<%= link_to 'New (sub-category', new_category_path(@category) unless @category.nil? %>
</p>
It shows the name of the selected category (or Main Category) and all of its sub-categories (in a nice table). It links to all sub-categories, showing a similar layout, but for the sub-category. In the end it adds a 'new sub-category' link and a 'back' link.