代码之家  ›  专栏  ›  技术社区  ›  Joseph Weissman

充当具有多个模型的树

  •  2
  • Joseph Weissman  · 技术社区  · 16 年前

    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_树下,每个节点可以单独与其他节点分层关联,前提是它们具有相同的类型。我想删除对类型标识的限制,这样SomePost.parent可以有一个用户或一个Post作为它的父项,SomeUser.parent可以有另一个用户或组作为它的父项。

    有什么想法吗?

    2 回复  |  直到 16 年前
        1
  •  3
  •   Toby Hede    16 年前

    我过去这样做的方式是使用树中的多态容器,映射到特定的单个模型。

    class Container < ActiveRecord::Base
       acts_as_tree
       belongs_to :containable, :polymorphic => true 
    end
    
    class User
      has_one :container :as => :containable
    end
    
        2
  •  0
  •   Translunar    16 年前

    我设法做了一点不同,但这可能不适合你的情况。我不想对现有的数据库进行重构,我也不想迁移任何代码。

    我在中的ClassMethods中添加了两个函数 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,它只是一个基类)。

    同样,这是非常特定于应用程序的。但它给了您一个如何修改acts_as_树的想法。

    推荐文章