代码之家  ›  专栏  ›  技术社区  ›  Christoph Schiessl Joeyjoejoejr

如何将嵌套集中的所有记录呈现到真正的HTML树中

  •  8
  • Christoph Schiessl Joeyjoejoejr  · 技术社区  · 15 年前

    我正在使用 awesome_nested_set 我的Rails项目中的插件。我有两个这样的模型(简化):

    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 ,请 lft rgt 是正确的。树有多个根节点(当然,在 太棒了,套了套 )

    现在,我要在一个正确排序的树状结构中呈现给定客户的所有类别:例如嵌套 <ul> 标签。这不会太困难,但我需要它高效(SQL查询越少越好)。

    更新: 计算出可以在不进行进一步的SQL查询的情况下计算树中任何给定节点的子节点数: number_of_children = (node.rgt - node.lft - 1)/2 . 这并不能解决问题,但可能会有所帮助。

    7 回复  |  直到 9 年前
        1
  •  7
  •   John F. Miller    15 年前

    如果嵌套集具有开箱即用的更好的特性,那就太好了,不是吗?

    正如您所发现的,技巧是从一个平面组构建树:

    • 从一组按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 
    
        2
  •  5
  •   Community rohancragg    7 年前

    我回答了 similar question for php 最近(嵌套集=modified preorder tree traversal model)。

    基本概念是通过以下方式获得已排序的节点和深度指示器: 一个SQL查询 .从这里开始,只是一个通过循环或递归呈现输出的问题,所以应该很容易将其转换为Ruby。

    我不熟悉 awesome_nested_set 插件,但它可能已经包含一个选项来获取深度注释的有序结果,因为在处理嵌套集时,这是一个相当标准的操作/需求。

        3
  •  5
  •   Sytse Sijbrandij    13 年前

    自2009年9月以来,Awesome嵌套集包含了一种特殊的方法: https://github.com/collectiveidea/awesome_nested_set/commit/9fcaaff3d6b351b11c4b40dc1f3e37f33d0a8cbe

    这个方法比调用level有效得多,因为它不需要任何额外的数据库查询。

    示例:category.each_with_level(category.root.self_and_descendants)do_o,level|

        4
  •  3
  •   François Beausoleil    15 年前

    您必须递归地呈现一个称为自身的部分。像这样:

    # 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代码。你必须先打电话给路由,然后明确地说出部分的名字。

        5
  •  3
  •   Anton Orel    14 年前

    T.H.ML.EB

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

    I.EM.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 %>
    
        6
  •  1
  •   chyno    10 年前

    我想是因为写这本书的鲁比的旧版本,我无法得到公认的答案。以下是适合我的解决方案:

    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
    

    通过使用 可选择的 深度信息。 (这种方法的优点是不需要输入集是叶的整个结构。)

    更复杂的无深度解决方案可以在gem的github wiki上找到:

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

        7
  •  0
  •   troex    9 年前

    也许有点晚了,但我想分享我的解决方案 awesome_nested_set 基于 closure_tree 宝石嵌套 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