views:

27

answers:

3

How to implement in rails a category that can be child of another category (self-referential has_one) ?

Thanks

A: 

Add a column category_id to the categories table and then tell the Category model, that it belongs to itself:

class Category < ActiveRecord::Base
  belongs_to :category
end

For clarity's sake, you might want to name the association child instead of category, so you can name the column child_id and then do:

class Category < ActiveRecord::Base
  belongs_to :child, :class_name => :category
end
sepp2k
Thanks for answering. Your answer is correct but the other answer I accepted explains only a bit further. Thank you for your time and answer.
Bruno Carvalho
+1  A: 

First, I believe you want a has_many relationship, not a has_one. I can't imagine a situation where you would want categories to have at most one child category. The previous answer also only gets you one direction - categories know about their parents, but not about their children.

The full solution is simple enough. The categories table should have a category_id column, and the model should look like this:

class Category < ActiveRecord::Base
  belongs_to :category
  has_many :categories
end

If you want to go a step further and call them parents and children, you can:

class Category < ActiveRecord::Base
  belongs_to :parent, :class_name => 'Category', :foreign_key => 'category_id'
  has_many :children, :class_name => 'Category', :foreign_key => 'category_id'
end

Good luck with your app!

Jaime Bellmyer
Thank you, this worked pretty well. I was a bit confused on this association and your answer clarified it all :) Thanks.
Bruno Carvalho
A: 

The simple approach is to use a foreign key of category_id as others have already pointed out. However, if you're talking about nesting lots of categories this can be pretty inefficient. (Note: Posting more of your requirements would be helpful.)

I really, really like the ancestry gem. Here is a relevant snip from the docs (emphasis mine).

As can be seen in the previous section, Ancestry stores a path from the root to the parent for every node. This is a variation on the materialised path database pattern. It allows Ancestry to fetch any relation (siblings, descendants, etc.) in a single sql query without the complicated algorithms and incomprehensibility associated with left and right values. Additionally, any inserts, deletes and updates only affect nodes within the affected node’s own subtree.

And, here's a bonus freebie, just because it's not immediately obvious. If you need to treat siblings as a list with positions, you can scope them as follows.

acts_as_list :scope => 'ancestry #{(ancestry.blank? ? "IS NULL" : "=\'" + ancestry + "\'")}'
jdl
Thank you jdl, I'll look onto this but actually I have a pretty simple requirement to do category and subcategory of a simple system of products. I'll keep in mind this gem when I need more complicated structures like you said :) thanks for pointing me to it !
Bruno Carvalho