2

階層的に関連付けたいモデルがいくつかあります。簡単にするために、私がこれらの3つを持っているとしましょう:

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では、同じタイプであれば、各ノードを個別に他のノードに階層的に関連付けることができます。私が望むのは、タイプIDに関するこの制限を削除して、SomePost.parentがその親としてUserまたはPostを持つことができ、SomeUser.parentがその親として別のユーザーまたはグループを持つことができるようにすることです。

何かご意見は?

4

2 に答える 2

3

私が過去にこれを行った方法は、ツリー内に存在する多態的なコンテナーを使用して、特定の個々のモデルにマッピングすることです。

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

class User
  has_one :container :as => :containable
end
于 2010-01-26T22:28:13.087 に答える
0

私はそれを少し違ったやり方で行うことができましたが、これはあなたの状況ではうまくいかないかもしれません。私は既存のコードをリファクタリングしていて、深刻なデータベースの移行をしたくありませんでした。

リーフクラスとノードクラスを別々にしたかったのです。どちらもツリークラスから継承します。

ClassMethodsに2つの関数を追加しました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で、次の1つの関数を追加しました。

    # 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

これはちょっとしたハックですが、すべてのノードにすべて1つのタイプのサブがあるので、私にとってはうまくいきます。このchildren関数を試して、次のようなさまざまな動作を取得できます。

def children
  child_nodes | child_leaves
end

ただし、それでも追加のクエリが必要になり、順序やスコープなどが失われます。

最後に、私のNodeクラスでは、

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

そして私のLeafクラスでは、次のようになります。

acts_as_tree_leaf :node_class_name => 'NodeMatrix'

これらは両方とも、純粋な仮想であるTreeMatrixから継承します(TreeMatrixとして実際にインスタンス化されるものはなく、単なる基本クラスです)。

繰り返しますが、これは非常にアプリケーション固有です。ただし、acts_as_treeを変更する方法がわかります。

于 2010-04-02T23:15:17.000 に答える