Site Navigation With Polymorphic Has Many Through

Hi, seem to keep running into a wall here. I can’t find any resources on
site navigation that can deal with any model being in the nav, allow
nesting, and can dynamically update.

So I thought about it for a while, and decided on a MenuItems class,
which
contained the position of the child in relation to it’s siblings, where
the
parent and the child were polymorphic. Then a given childable object can
find it’s parentable by going through the menu_items table.

I want my nav to be able to do things like this:
–Category1
----SubCategory1
------Product1
------Product2
----Product3
–Category2
----Product4
–Page1
–Page2
–Page3

This is the current setup:

MODELS

class MenuItem < ActiveRecord::Base
belongs_to :childable , :polymorphic => true
belongs_to :parentable , :polymorphic => true

acts_as_list :scope => :parentable_id
end

class Category < ActiveRecord::Base
has_one :parent_menu_item , :as => :parentable , :class_name =>
‘MenuItem’
has_many :child_menu_items , :as => :childable , :class_name =>
‘MenuItem’

has_one :parentable , :through => :parent_menu_item
has_many :childables , :through => :child_menu_items
end

class SubCategory < ActiveRecord::Base
has_many :child_menu_items , :as => :childable , :class_name =>
‘MenuItem’
has_one :parent_menu_item , :as => :parentable , :class_name =>
‘MenuItem’

has_one :parent , :through => :parent_menu_item
has_many :children , :through => :child_menu_items
end

class Page < ActiveRecord::Base
has_one :parent_menu_item , :as => :parentable , :class_name =>
‘MenuItem’
has_one :parent , :through => :parent_menu_item
end

SCHEMA:
ActiveRecord::Schema.define(:version => 20100525184637) do

create_table “categories”, :force => true do |t|
t.datetime “created_at”, :null => false
t.datetime “updated_at”, :null => false
end

create_table “menu_items”, :force => true do |t|
t.integer “position”, :null => false
t.integer “parentable_id”, :null => false
t.string “parentable_type”, :null => false
t.integer “childable_id”, :null => false
t.string “childable_class”, :null => false
t.datetime “created_at”, :null => false
t.datetime “updated_at”, :null => false
end

create_table “pages”, :force => true do |t|
t.datetime “created_at”, :null => false
t.datetime “updated_at”, :null => false
end

create_table “sub_categories”, :force => true do |t|
t.datetime “created_at”, :null => false
t.datetime “updated_at”, :null => false
end

end

I have had a lot of trouble with it, this is the best I’ve gotten so
far,
but I am still getting the error:
ActiveRecord::HasManyThroughAssociationPolymorphicError: Cannot have a
has_many :through association ‘Category#childables’ on the polymorphic
object ‘Childable#childable’.

Is there a way to make this work? Is there a better way to do this?

Josh,
We’re doing something similar, except that instead of using childable
and parentable, we’re using awesome_nested_set. It enables you to
have sub_categories within sub_categories, etc., and its interface is
pretty intuitive and efficient:

http://wiki.github.com/collectiveidea/awesome_nested_set/awesome-nested-set-cheat-sheet

Tilde E.

On Wed, May 26, 2010 at 3:09 AM, Tilde E. [email protected]
wrote:

Tilde E.
the

----Product4
belongs_to :parentable , :polymorphic => true
has_one :parentable , :through => :parent_menu_item
has_many :children , :through => :child_menu_items

t.string   "childable_class", :null => false
t.datetime "created_at", :null => false

http://groups.google.com/group/rubyonrails-talk?hl=en.

I appreciate the suggestion, I had previously used awesomer nested set
on a
project. I had two issues with it. The first was that the reason for
using
it was so that you could query for any level of children with one db
query,
but when I then went to ask each element for it’s children, I found that
it
was re-querying, so this purpose was unrealized. (possibly this
behaviour
has been resolved?)

The way to get around it was to transform the results into something
usable,
ie hashes of hashes, or arrays of arrays, etc. But this was not provided
by
the plugin, and coupled a lot of code. In the end, I spent about 8 hours
writing a method that would iterate through the results one time,
accepting
lambdas for what to do before entering a deeper level, after leaving it,
and
what should be done to display a single element. It got pretty ugly, and
may
have been an example of premature optimization, probably caching would
have
made the large number of database queries irrelevant, but the real
reason I
had to do that at all was to mimic a tree like structure, which is what
a
many to many association already provides.

So I don’t really see much benefit to the nested sets data structure.
Also,
I’m not sure how well it integrates with polymorphism. In my case,
Categories and SubCategories will have different attributes, and Pages
will
be drastically different than either of these. They all need to be in
the
navigation. In my previous attempt, I queried for each object that the
menu
item was representing, but again, that seems inefficient. To be fair,
though, when I tried last, i was still very new to Ruby and Rails.

I thought a polymorphic has_many :through would keep things clean, give
me
the tree structure I need for creating nested

    in my html, and dry
    up
    the associations between elements. Of course, it doesn’t have the
    inherent
    ordering that nested sets do, which is why I also have acts_as_list
    positions on the join table.

    At present, I’m probably going to give this plugin a try:
    http://github.com/fauna/has_many_polymorphs/tree/bcd9626411c9d0658ab527f1e6a0d0622e4f6e15

    If it goes well, I’ll report back here.
    If my above statements represented an incorrect assessment, please
    correct
    me.
    If there is some obvious solution that I am missing, please guide me to
    it.

    -Josh