2009-09-03 58 views
8

我在我的Rails項目中使用awesome_nested_set插件。我有兩個看起來像這樣的模型(簡化版):如何將所有記錄從嵌套集合轉換爲真正的html樹

class Customer < ActiveRecord::Base 
    has_many :categories 
end 

class Category < ActiveRecord::Base 
    belongs_to :customer 

    # Columns in the categories table: lft, rgt and parent_id 
    acts_as_nested_set :scope => :customer_id 

    validates_presence_of :name 
    # Further validations... 
end 

數據庫中的樹按預期構建。 parent_id,lftrgt的所有值都是正確的。該樹有多個根節點(當然允許在awesome_nested_set中)。

現在,我想要呈現給定客戶的所有類別,如正確排序的樹狀結構:例如嵌套的<ul>標籤。這不會太困難,但我需要它是有效的(越少的SQL查詢越好)。

更新:找出有可能計算沒有進一步SQL查詢的樹中任何給定節點的孩子數:number_of_children = (node.rgt - node.lft - 1)/2。這並不能解決問題,但它可能被證明是有幫助的。

回答

7

如果嵌套套件具有更好的功能,那就好了。

因爲你已經發現了這個竅門是從平面組建立樹:

  • 開始與LFT排序的集合中的所有節點的
  • 的第一個節點是根將其添加爲根樹移動到下一個節點
  • 如果是前一個節點的孩子(prev.lft和prev.rht之間LFT)添加子樹和向前移動一個節點
  • 否則移樹一個水平和重複測試

見下圖:

def tree_from_set(set) #set must be in order 
    buf = START_TAG(set[0]) 
    stack = [] 
    stack.push set[0] 
    set[1..-1].each do |node| 
    if stack.last.lft < node.lft < stack.last.rgt 
     if node.leaf? #(node.rgt - node.lft == 1) 
     buf << NODE_TAG(node) 
     else 
     buf << START_TAG(node) 
     stack.push(node) 
     end 
    else# 
     buf << END_TAG 
     stack.pop 
     retry 
    end 
    end 
    buf <<END_TAG 
end 

def START_TAG(node) #for example 
    "<li><p>#{node.name}</p><ul>" 
end 

def NODE_TAG(node) 
    "<li><p>#{node.name}</p></li>" 
end 

def END_TAG 
    "</li></ul>" 
end 
+0

這可以工作。你對awesome_nested_set也是對的。我忍不住想知道爲什麼這不是首先嵌入到插件中的。謝謝! – 2009-09-05 18:10:43

+0

忘了提及:關於您的解決方案的要點是,它只需要一個SQL查詢! – 2009-09-05 18:11:58

+3

http://gist.github.com/460814 – 2010-07-02 01:41:55

3

你必須遞歸渲染一個將自己調用的部分。類似這樣的:

# customers/show.html.erb 
<p>Name: <%= @customer.name %></p> 
<h3>Categories</h3> 
<ul> 
    <%= render :partial => @customer.categories %> 
</ul> 

# categories/_category.html.erb 
<li> 
    <%= link_to category.name, category %> 
    <ul> 
    <%= render :partial => category.children %> 
    </ul> 
</li> 

這是Rails 2.3的代碼。您必須在此之前調用路線並明確命名部分。

+1

是啊,我想出了相同的解決方案我自己。問題是每次調用'children'都會執行額外的SQL查詢(100個子樹= 100個SQL查詢)。導致經典的N + 1問題。這正是我想要避免的。此外:第一個渲染部分調用應該像'<%= render:partial => @ customer.categories.roots%>' – 2009-09-03 12:36:30

5

最近我回答了一個similar question for php(嵌套集==修改的預置樹遍歷模型)。

基本概念是通過一個SQL查詢得到已經訂購的節點和深度指示符。從那裏它只是通過循環或遞歸呈現輸出的問題,所以應該很容易將其轉換爲ruby。

我不熟悉awesome_nested_set插件,但它可能已經包含一個選項來獲取深度註釋的有序結果,因爲在處理嵌套集合時它是一個非常標準的操作/需要。

3

_tree.html.eb

@set = Category.root.self_and_descendants 
<%= render :partial => 'item', :object => @set[0] %> 

_item.html。ERB

<% @set.shift %> 
<li><%= item.name %> 
<% unless item.leaf? %> 
<ul> 
    <%= render :partial => 'item', :collection => @set.select{|i| i.parent_id == item.id} %> 
</ul> 
<% end %> 
</li> 

您還可以排序的:

<%= render :partial => 'item', :collection => @set.select{|i| i.parent_id == item.id}.sort_by(&:name) %> 

但在這種情況下,你應該刪除此行:

<% @set.shift %> 
1

我不能去接受的答案,因爲舊版本的紅寶石是寫的,我想。下面是該解決方案爲我工作:

def tree_from_set(set) 
    buf = '' 

    depth = -1 
    set.each do |node| 
     if node.depth > depth 
      buf << "<ul><li>#{node.title}" 
     else 
      buf << "</li></ul>" * (depth - node.depth) 
      buf << "</li><li>#{node.title}" 
     end 

     depth = node.depth 
    end 

    buf << "</li></ul>" * (depth + 1) 

    buf.html_safe 
end 

它使用可選深度信息簡化。 (這種方法的優點是,有沒有必要的輸入設置爲整體結構的葉子),無深度

更復雜的解決方案可以在創業板的GitHub的wiki上找到:

https://github.com/collectiveidea/awesome_nested_set/wiki/How-to-generate-nested-unordered-list-tags-with-one-DB-hit

0

也許有點晚了,但我想分享我的解決方案基於closure_tree寶石awesome_nested_set嵌套hash_tree方法:

def build_hash_tree(tree_scope) 
    tree = ActiveSupport::OrderedHash.new 
    id_to_hash = {} 

    tree_scope.each do |ea| 
    h = id_to_hash[ea.id] = ActiveSupport::OrderedHash.new 
    (id_to_hash[ea.parent_id] || tree)[ea] = h 
    end 
    tree 
end 

這將通過lft

下令不是使用助手來解析它的任何範圍的工作:

def render_hash_tree(tree) 
    content_tag :ul do 
    tree.each_pair do |node, children| 
     content = node.name 
     content += render_hash_tree(children) if children.any? 
     concat content_tag(:li, content.html_safe) 
    end 
    end 
end 
相關問題