Forum: Ruby on Rails Using acts_as_tree and belongs_to together

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
nmkolev (Guest)
on 2005-12-05 15:49
(Received via mailing list)
Hi all,

I sat down to rework one of my first rails apps which was a very,
very simple store. The organisation of the products in the store was
rather flat:

class Category < AR:B
   has_many :products
end

class Product < AR:B
   belongs_to :category
end

I want to do it with arbitrary category nesting this time--a category
can have a subcategory, this on the other hand can have other
subcategories and so on. Only categories that have no other
subcategories can hold products. So far, so good:

class Category < AR:B
   acts_as_tree
   has_many :products
end

class Product < AR:B
   belongs_to :category
end

But how do I prevent users from entering products in categories that
have subcategories? Also, how do I present the user with an interface
for selecting the parent category in the new-product form? Nested
select elements perhaps (one select with all the 'root' categories,
selecting a root category triggers the appearance of another select
element with its subcategories and so on until the last selected
category has no more children)?

Many thanks in advance for all pointers!

Nickolay
mrj (Guest)
on 2005-12-06 04:41
(Received via mailing list)
Nickolay K. wrote:

> class Product < AR:B
>   belongs_to :category
> end


I'd suggest either having a separate LeafCategory model (which shares
Category model's ids (no auto_increment), to which the product belongs,
and which itself belongs to the Category model), or alternatively have
a field in the Category model designating that category as a leaf
category.


> But how do I prevent users from entering products in categories that
> have subcategories? Also, how do I present the user with an interface
> for selecting the parent category in the new-product form? Nested
> select elements perhaps (one select with all the 'root' categories,
> selecting a root category triggers the appearance of another select
> element with its subcategories and so on until the last selected
> category has no more children)?

I've written a combination of a helper and an AJAXified component
that implements chained selects for category selection.  You just
put a call to the helper in a form in your view:

    category_selector( object, method, options = {}, html_options = {} )

e.g.

<%= category_selector( :product, :category_id,
       {:model => :product_category, :all => true, :on_change =>
{:controller => 'store', :action => 'category_change'}},
       :class => 'catsel' ) %>


The :all option specifies whether the selected category (is put in a
hidden field)
must be a leaf category, or whether non-leaf ("all categories"/"all
subcategories")
can be selected.

It seems to be working fine, but I wasn't planning to release it until I
had
tested it further and implemented an indication that communication with
the
server was in progress.

I also wanted to have a different mode of operation, specified in the
helper options, in which the whole category tree is loaded into
JavaScript
so the appearance and disappearance of select levels is virtually
instantaneous
after an initial load time.  You could even get the helper to
automatically
choose the best method based on the size of the category tree and the
speed
and latency of the link.


--
We develop, watch us RoR, in numbers too big to ignore.
mrj (Guest)
on 2005-12-06 05:13
(Received via mailing list)
Nickolay K. wrote:

> But how do I prevent users from entering products in categories that
> have subcategories?

Of course the simplest method is to validate incoming category_ids with
         Category.find(category_id).children.empty?

--
We develop, watch us RoR, in numbers too big to ignore.
nmkolev (Guest)
on 2005-12-06 10:30
(Received via mailing list)
>> But how do I prevent users from entering products in categories
>> that  have subcategories?
>
> Of course the simplest method is to validate incoming category_ids
> with
>         Category.find(category_id).children.empty?

I was thinking:

class Category < AR:B
   ..
   def is_leaf?
     0 == children.size
   end
end

class Product < AR:B
   ..
   private
   def validate
     errors.add(:category_id, 'Category schould be a leaf') unless
Category.find(@params[:category_id]).is_leaf?
   end
end

This (or something along those lines) would prevent Product object
from being saved if they do not belong to a leaf category. But I find
this should be enforced in the edit/create form as well. I am very
interested in the category_selector helper if you care to post it.
Perhaps we could iron out some stuff together. :-)

Cheers,
-- Nicky
This topic is locked and can not be replied to.