2010-01-26 4 views
2

У меня есть несколько моделей, которые я бы хотел связать иерархически. Для простоты предположим, что у меня есть эти три:Действует как дерево с несколькими моделями

class Group < ActiveRecord::Base 
    acts_as_tree 
    has_many :users 
end 

class User < ActiveRecord::Base 
    acts_as_tree 
    belongs_to :group 
    has_many :posts 
end 

class Post < ActiveRecord::Base 
    acts_as_tree 
    belongs_to :user 
end 

Согласно действующему acts_as_tree, каждый узел может индивидуально может относиться иерархически к другим узлам при условии, что они имеют одинаковый тип. Я хотел бы удалить это ограничение на идентификатор типа, так что SomePost.parent может иметь пользователя или сообщение в качестве своего родителя, а SomeUser.parent может иметь другого пользователя или группу в качестве родителя.

Любые мысли?

ответ

3

То, как я это делал в прошлом, заключается в использовании полиморфного контейнера, который живет в дереве, сопоставления с конкретными отдельными моделями.

class Container < ActiveRecord::Base 
    acts_as_tree 
    belongs_to :containable, :polymorphic => true 
end 

class User 
    has_one :container :as => :containable 
end 
+0

Отлично! Это отлично поработало для меня. Спасибо ... –

+0

это означает, что сообщение и группа будут иметь has_one: container: as =>: сдерживаемый? – corroded

0

Мне удалось сделать это по-другому, но это может не сработать для вашей ситуации. Я рефинансировал существующий код и не хотел, чтобы какая-либо серьезная миграция базы данных.

Мне нужны отдельные классы листьев и узлов. Оба наследуются от древовидного класса.

Я добавил две функции в методы класса в vendor/plugins/acts_as_tree/lib/active_record/acts/tree.rb:

# Configuration options are: 
    # 
    # * <tt>foreign_key</tt> - specifies the column name to use for tracking of the tree (default: +parent_id+) 
    # * <tt>order</tt> - makes it possible to sort the children according to this SQL snippet. 
    # * <tt>counter_cache</tt> - keeps a count in a +children_count+ column if set to +true+ (default: +false+). 
    # * <tt>leaf_class_name</tt> - leaf class subtype of base tree class 
    # * <tt>node_class_name</tt> - node class subtype of base tree class 
    def acts_as_tree_node(options = {}) 
     configuration = { :foreign_key => "parent_id", :order => nil, :counter_cache => nil, :node_class_name => 'Node', :leaf_class_name => 'Leaf' } 
     configuration.update(options) if options.is_a?(Hash) 

     belongs_to :parent, :class_name => configuration[:node_class_name], :foreign_key => configuration[:foreign_key], :counter_cache => configuration[:counter_cache] 
     #has_many :children, :foreign_key => configuration[:foreign_key], :order => configuration[:order], :dependent => :destroy 

     class_eval <<-EOV 
     has_many :child_nodes, :class_name => '#{configuration[:node_class_name]}', :foreign_key => "#{configuration[:foreign_key]}", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}}, :dependent => :destroy 
     has_many :child_leaves, :class_name => '#{configuration[:leaf_class_name]}', :foreign_key => "#{configuration[:foreign_key]}", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}}, :dependent => :destroy 

     include ActiveRecord::Acts::Tree::InstanceMethods 

     def self.roots 
      find(:all, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}}) 
     end 

     def self.root 
      find(:first, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}}) 
     end 
     EOV 
    end 

    # Configuration options are: 
    # 
    # * <tt>foreign_key</tt> - specifies the column name to use for tracking of the tree (default: +parent_id+) 
    # * <tt>order</tt> - makes it possible to sort the children according to this SQL snippet. 
    # * <tt>counter_cache</tt> - keeps a count in a +children_count+ column if set to +true+ (default: +false+). 
    # * <tt>node_class_name</tt> - the class name of the node (subclass of the tree base) 
    def acts_as_tree_leaf(options = {}) 
     configuration = { :foreign_key => "parent_id", :order => nil, :counter_cache => nil, :node_class_name => 'Node' } 
     configuration.update(options) if options.is_a?(Hash) 

     belongs_to :parent, :class_name => configuration[:node_class_name], :foreign_key => configuration[:foreign_key], :counter_cache => configuration[:counter_cache] 

     class_eval <<-EOV 
     include ActiveRecord::Acts::Tree::InstanceMethods 

     def self.roots 
      find(:all, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}}) 
     end 

     def self.root 
      find(:first, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}}) 
     end 
     EOV 
    end 

Затем в InstanceMethods, я добавил одну функцию:

# Returns list of children, whether nodes or leaves. 
    # 
    # NOTE: Will not return both, because that would take two queries and 
    # order will not be preserved. 
    def children 
     child_leaves.count == 0 ? child_nodes : child_leaves 
    end 

Это немного рубить, но это работает для меня поскольку каждый узел имеет один тип подмножеств. Вы можете играть с функцией children, чтобы получить другое поведение, например, следующим образом:

def children 
    child_nodes | child_leaves 
end 

Но он по-прежнему принимает дополнительный запрос, и вы потеряете ваш заказ и областей и прочее.

Наконец, в моем классе Node, у меня есть

acts_as_tree_node :node_class_name => 'NodeMatrix', :leaf_class_name => 'LeafMatrix' 

и в моем классе листьев, следующие:

acts_as_tree_leaf :node_class_name => 'NodeMatrix' 

Оба эти наследуют от TreeMatrix, который является чисто виртуальным (нет ничего фактически созданный как TreeMatrix, это просто базовый класс).

Опять же, это очень специфично для конкретного приложения. Но это дает вам представление о том, как вы можете изменять act_as_tree.