Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Structure Multiple Levels of Hierarchy in Ruby on Rails?

A rails app contains many different Pages of content. The Pages are organized into small groups known as Sections:

class Page < ActiveRecord::Base
   attr_accessible: section_id #etc..
   belongs_to :section
end

class Section < ActiveRecord::Base
  attr_accessible :title #, etc... 
  has_many :pages
end

The Sections need to be organized too, but what is the best way to do this - re-use sections themselves, or create a new Unit model?

Option 1 - Re-use Section
Allow Section to have children and parent Sections. That way, you don't need to create another model with similar fields as Section. Some sections will have_many pages, and other sections will have_many children sections:

class Section < ActiveRecord::Base
  attr_accessible :parent_id  :title # etc... 
  has_many :pages

  belongs_to :parent, class_name: "Section"
  has_many :children, class_name: "Section", foreign_key: "parent_id"
end

Option 2 - new Unit model
Create another model called Unit to organize the sections. It will have many similar fields to section, but it will be a clearly separate entity.

class Section < ActiveRecord::Base
  attr_accessible :title, :unit_id # etc... 
  has_many :pages
  belongs_to :units
end

class Unit < ActiveRecord::Base
  attr_accessible :title # etc... 
  has_many :sections
end

The advantage of Option 1 is it avoid some duplication, and could be adapted in the future if even more levels are needed. However, Option 2 clearly separates the roles of Sections, which have_many pages, from Units, which have_many Sections, which can help keep other code clear. Which is the best approach?

Update
It seems Option 2 would have clearer code, such as when going through all the Sections. Is it worth re-using Sections if it would make some code more complicated? For example, here's how to list all the Sections in an organized manner:

Option 2 - For each Unit, list all the child sections. Then list any Sections that aren't in any Unit.

Option 1 - For each parent Section, list all the children Sections. Then list any Section that has with no parent Section or child Section.

like image 786
am-rails Avatar asked Nov 26 '13 16:11

am-rails


2 Answers

It will be worth reusing Section(using Option 1), if you see Section and its children having exactly the same methods defined in them. Otherwise, you should go for Option 2.

Regarding the concerns you had on how to list all the Sections in an organized manner:

Option 1 - This isn't and can be done unless you want to iterate through one collection which has the parent section and the children sections. See how we could do some of the queries in ActiveRecord below:

sections_with_parent = Section.joins(:parent)
sections_with_children = Section.joins(:children).uniq
parent_key_with_children_values = Section.joins(:children).uniq.inject({}) do |result, section|
  result.merge({section => section.children})
end
sections_with_no_parent = Section.where(parent_id: nil)

Option 2 - Below are some code for comparison with above:

sections_with_parent = Section.joins(:unit)
units_with_children = Unit.joins(:sections).uniq
parent_key_with_children_values = Unit.joins(:sections).uniq.inject({}) do |result, unit|
  result.merge({unit => unit.sections })
end
sections_with_no_parent = Section.where(unit_id: nil)

As you can see, both options will have very similar code for listing children and parents so that shouldn't be a concern when deciding on what option to go for.

like image 160
Gjaldon Avatar answered Nov 16 '22 19:11

Gjaldon


It really depends on how far you want to go. If it's just one extra level of hierarchy, then definitely go with the new model. If you're looking to be able to go 2+ levels deep, definitely go with the option of reusing sections.

like image 31
kddeisz Avatar answered Nov 16 '22 20:11

kddeisz