Getting nested set model into a
    but hiding “closed” subtrees

前端 未结 8 1191
北海茫月
北海茫月 2020-12-14 13:02

Based on Getting a modified preorder tree traversal model (nested set) into a

    One of answers gave right code to display full tree. What i need is to alway

相关标签:
8条回答
  • 2020-12-14 13:49

    Based on answer by satrun77. I created a helper for symfony + doctrine + nestedset (http://www.doctrine-project.org/projects/orm/1.2/docs/manual/hierarchical-data/en):

    function render_tree_html_list($nodes, Doctrine_Record $current_node, $render = true) {
        $html = '';
        $current_node_level = $current_node->getLevel();
        $counter = 0;
        $found = false;
        $nextSibling = false;
    
        foreach ($nodes as $i => $node):
            $node_level = $node->getLevel();
            $node_name = $node->getTitulo();
            $node_id = $node->getId();
    
            if ($current_node !== false) {
                if ($node_level == 0) {
    
                    if ($node->getLft() <= $current_node->getLft() && $node->getRgt() >= $current_node->getRgt()) {
                        // selected root item
                        $root = $node;
                    }
                } else if (!isset($root)) {
                    // skip all items that are not under the selected root
                    continue;
                } else {
                    // when selected root is found
    
                    $isInRange = ($root->getLft() <= $node->getLft() && $root->getRgt() >= $node->getRgt());
                    if (!$isInRange) {
                        // skip all of the items that are not in range of the selected root
                        continue;
                    } else if ($current_node->getLft() && $node->getLft() == $current_node->getLft()) {
                        // selected item reached
                        $found = true;
                        $current_node = $node;
                    } else if ($nextSibling !== false && $nextSibling->getLevel() < $node->getLevel()) {
    
                        // if we have siblings after the selected item
                        // skip any other childerns in the same range or the selected root item
                        continue;
                    } else if ($found && $node_level == $node->getLevel()) {
                        // siblings after the selected item
                        $nextSibling = $node;
                    }
                }
            } else if ($node_level > 0) {
                // show root items only if no childern is selected
                continue;
            }
    
            if ($node_level == $current_node_level) {
                if ($counter > 0)
                    $html .= '</li>';
            }
            elseif ($node_level > $current_node_level) {
                $html .= '<ol>';
                $current_node_level = $current_node_level + ($node_level - $current_node_level);
            } elseif ($node_level < $current_node_level) {
                $html .= str_repeat('</li></ol>', $current_node_level - $node_level) . '</li>';
                $current_node_level = $current_node_level - ($current_node_level - $node_level);
            }
    
            $html .= sprintf('<li node="%d" class="%s"><div>%s</div>',
                    $node_id,
                    (isset($nodes[$i + 1]) && $nodes[$i + 1]->getLevel() > $node_level) ? "node" : "leaf",
                    $node->getLevel() > 0 ? link_to($node->getTitulo(), 'cms_categoria_edit', $node) : $node->getTitulo()
            );
    
            ++$counter;
        endforeach;
    
        $html .= str_repeat('</li></ol>', $node_level) . '</li>';
        $html = '<ol class="sortable">'. $html .'</ol>';
    
    
        return $render ? print($html) : $html;
    }
    

    Extra tags: tree, node

    0 讨论(0)
  • 2020-12-14 13:51

    As you already managed to sort the sequence, why not just output as needed?

    As some leafs need to appear closed, so the iterator should be able to skip children of non-selected nodes.

    Doing so lead me to an idea to solve the problem of terminating the output tree (output = parsing). What to do if the last valid node in the sequence is at a higher depth than 0? I appended a NULL terminator for that. So still open levels can be closed before the loop finishes.

    Additionally the iterator overloads nodes to offer common methods on them, like comparing against the currently selected element.

    The MyRenderTree function (Demo/Full code)

    Edit: The Demo Codepad has problems, here is the source-code: Gist Getting nested set model into a but hiding “closed” subtrees

    function MyRenderTree($tree = array(array('name'=>'','depth'=>'', 'lft'=>'','rgt'=>'')) , $current=false)
    {
        $sequence = new SequenceTreeIterator($tree);
    
        echo '<ul>';
        $hasChildren = FALSE;
        foreach($sequence as $node)
        {
            if ($close = $sequence->getCloseLevels())
            {
                echo str_repeat('</ul></li>', $close);
                $hasChildren = FALSE;
            }
            if (!$node && $hasChildren)
            {
                echo '</li>', "\n";
            }
            if (!$node) break; # terminator
    
            $hasChildren = $node->hasChildren();
            $isSelected = $node->isSupersetOf($current);
    
            $classes = array();
            $isSelected && ($classes[] = 'selected') && $hasChildren && $classes[] = 'open';
            $node->isSame($current) && $classes[] = 'current';
    
            printf('<li class="%s">%s', implode(' ', $classes), $node['name']);
    
            if ($hasChildren)
                if ($isSelected)
                    echo '<ul>';
                else
                    $sequence->skipChildren()
                ;
            else
                echo '</li>'
            ;
        }
        echo '</ul>';
    }
    

    This can be solved as well in a single foreach and some variables, however I think for re-useablilty, the implementation based on the SPL Iterators is better.

    0 讨论(0)
提交回复
热议问题