将嵌套集合模型放入
    中但隐藏“封闭”子树

发布于 2024-12-09 04:09:55 字数 2483 浏览 1 评论 0原文

基于 获取修改后的先序树遍历模型(嵌套集)到

答案之一给出了正确的代码来显示完整的树。我需要的是始终显示活动列表项的第一级(深度=0)和兄弟姐妹+子级。目标是当用户选择更多列表项的父级列表项时展开树的可见部分。

所以,如果我得到这个列表:

1. item
2. item
  2.1. item
  2.2. item
    2.2.1. item
    2.2.2. item
    2.2.3. item
  2.3. item
  2.4. item
    2.4.1. item
    2.4.2. item
3. item
4. item
  4.1. item
  4.2. item
    4.2.1. item
    4.2.2. item
5. item

如果当前列表项是“2.”,列表应该看起来像这样:

1. item
2. item // this needs class .selected
  2.1. item
  2.2. item
  2.3. item
  2.4. item
3. item
4. item
5. item

如果当前列表项是“2.2.”,列表应该看起来像这样:

1. item
2. item // this needs class .selected
  2.1. item
  2.2. item // this needs class .selected
    2.2.1. item
    2.2.2. item
    2.2.3. item
  2.3. item
  2.4. item
3. item
4. item
5. item

下面有一个可以工作的示例代码很适合我展示完整的树。我还添加了 lft/rgt/current 来解决我的问题。

<?php
function MyRenderTree ( $tree = array(array('name'=>'','depth'=>'', 'lft'=>'','rgt'=>'')) , $current=false){

   $current_depth = 0;
   $counter = 0;

   $result = '<ul>';

   foreach($tree as $node){
       $node_depth = $node['depth'];
       $node_name = $node['name'];
       $node_id = $node['category_id'];

       if($node_depth == $current_depth){
           if($counter > 0) $result .= '</li>';
       }
       elseif($node_depth > $current_depth){
           $result .= '<ul>';
           $current_depth = $current_depth + ($node_depth - $current_depth);
       }
       elseif($node_depth < $current_depth){
           $result .= str_repeat('</li></ul>',$current_depth - $node_depth).'</li>';
           $current_depth = $current_depth - ($current_depth - $node_depth);
       }
       $result .= '<li id="c'.$node_id.'"';
       $result .= $node_depth < 2 ?' class="open"':'';
       $result .= '><a href="#">'.$node_name.'</a>';
       ++$counter;
   }
   $result .= str_repeat('</li></ul>',$node_depth).'</li>';

   $result .= '</ul>';

   return $result;
}

// "$current" may contain category_id, lft, rgt for active list item
print MyRenderTree($categories,$current);
?>

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

One of answers gave right code to display full tree. What i need is to always show first level (depth=0) and siblings+childrens for active list item. Goal is to expand visible part of tree when user selects list item which is parent for more list items.

So, if i got this list:

1. item
2. item
  2.1. item
  2.2. item
    2.2.1. item
    2.2.2. item
    2.2.3. item
  2.3. item
  2.4. item
    2.4.1. item
    2.4.2. item
3. item
4. item
  4.1. item
  4.2. item
    4.2.1. item
    4.2.2. item
5. item

and if current list item is "2.", list should look like that:

1. item
2. item // this needs class .selected
  2.1. item
  2.2. item
  2.3. item
  2.4. item
3. item
4. item
5. item

and if current list item is "2.2.", list should look like that:

1. item
2. item // this needs class .selected
  2.1. item
  2.2. item // this needs class .selected
    2.2.1. item
    2.2.2. item
    2.2.3. item
  2.3. item
  2.4. item
3. item
4. item
5. item

Below there is an example code which works well for me to display full tree. I also added lft/rgt/current which will be needed to solve my issue.

<?php
function MyRenderTree ( $tree = array(array('name'=>'','depth'=>'', 'lft'=>'','rgt'=>'')) , $current=false){

   $current_depth = 0;
   $counter = 0;

   $result = '<ul>';

   foreach($tree as $node){
       $node_depth = $node['depth'];
       $node_name = $node['name'];
       $node_id = $node['category_id'];

       if($node_depth == $current_depth){
           if($counter > 0) $result .= '</li>';
       }
       elseif($node_depth > $current_depth){
           $result .= '<ul>';
           $current_depth = $current_depth + ($node_depth - $current_depth);
       }
       elseif($node_depth < $current_depth){
           $result .= str_repeat('</li></ul>',$current_depth - $node_depth).'</li>';
           $current_depth = $current_depth - ($current_depth - $node_depth);
       }
       $result .= '<li id="c'.$node_id.'"';
       $result .= $node_depth < 2 ?' class="open"':'';
       $result .= '><a href="#">'.$node_name.'</a>';
       ++$counter;
   }
   $result .= str_repeat('</li></ul>',$node_depth).'</li>';

   $result .= '</ul>';

   return $result;
}

// "$current" may contain category_id, lft, rgt for active list item
print MyRenderTree($categories,$current);
?>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

温柔一刀 2024-12-16 04:09:55

既然您已经成功地对序列进行了排序,为什么不根据需要输出呢?

由于某些叶子需要显示为关闭,因此迭代器应该能够跳过未选定节点的子节点。

这样做使我产生了解决终止输出树(输出=解析)问题的想法。如果序列中最后一个有效节点的深度大于 0 该怎么办?我为此附加了一个 NULL 终止符。因此,在循环结束之前可以关闭仍然打开的级别。

此外,迭代器会重载节点以在其上提供通用方法,例如与当前选定的元素进行比较。

MyRenderTree 函数(演示/完整代码

编辑: 演示键盘有问题,这里是源代码:要点
将嵌套集模型放入但隐藏“封闭”子树中

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>';
}

这也可以在单个 foreach 和一些变量中解决,但是我认为为了可重用,基于 SPL 迭代器 更好。

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.

暗地喜欢 2024-12-16 04:09:55

可以使用 Jquery,而不是使用 PHP 脚本来处理树导航。
一旦树生成,其余的事情将在客户端本身处理,它还将保存服务器请求。

请参阅示例 2 和 3

http://jquery.bassistance.de/treeview/demo/

< a href="http://docs.jquery.com/Plugins/Treeview" rel="nofollow">http://docs.jquery.com/Plugins/Treeview

它可能会根据您的要求有所帮助。

Instead of using PHP script for handling tree navigation, Jquery can be used.
Once the tree is generated rest of the things will handled on client itself, it will also save server requests.

See Sample 2 and 3

http://jquery.bassistance.de/treeview/demo/

http://docs.jquery.com/Plugins/Treeview

It may help as per your requirement.

婴鹅 2024-12-16 04:09:55

该函数期望 $tree 按“左”排序。

我已根据“左”和“右”值将您的功能修改为选定的项目。希望这就是您所追求的。

修改后的功能:

function MyRenderTree($tree = array(array('name' => '', 'depth' => '', 'lft' => '', 'rgt' => '')), $current=false)
    {
        $current_depth = 0;
        $counter = 0;
        $found = false;
        $nextSibling = false;
        $result = '<ul>';
        foreach ($tree as $node) {
            $node_depth = $node['depth'];
            $node_name = $node['name'];
            $node_id = 1;//$node['category_id'];

            if ($current !== false) {

                if ($node_depth ==0) {

                    if ($node['lft'] <= $current['lft'] && $node['rgt'] >= $current['rgt']) {
                        // 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['lft'] <= $node['lft'] && $root['rgt'] >= $node['rgt']);
                    if (!$isInRange) {
                        // skip all of the items that are not in range of the selected root
                        continue;
                    } else if (isset($current['lft']) && $node['lft'] == $current['lft']) {
                        // selected item reached
                        $found  = true;
                        $current = $node;
                    } else if ($nextSibling !== false && $nextSibling['depth'] < $node['depth']) {

                        // 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_depth == $node['depth']) {
                        // siblings after the selected item
                        $nextSibling = $node;
                    }
                }
            } else if ($node_depth > 0) {
                // show root items only if no childern is selected
                continue;
            }

            if ($node_depth == $current_depth) {
                if ($counter > 0)
                    $result .= '</li>';
            }
            elseif ($node_depth > $current_depth) {

                $result .= '<ul>';
                $current_depth = $current_depth + ($node_depth - $current_depth);
            } elseif ($node_depth < $current_depth) {

                $result .= str_repeat('</li></ul>', $current_depth - $node_depth) . '</li>';
                $current_depth = $current_depth - ($current_depth - $node_depth);
            }
            $result .= '<li id="c' . $node_id . '" ';
            $result .= $node_depth < 2 ?' class="open"':'';
            $result .= '><a href="#">' . $node_name .'(' . $node['lft'] . '-' . $node['rgt'] . ')' . '</a>';
            ++$counter;
        }
        unset($found);
        unset($nextSibling);

        $result .= str_repeat('</li></ul>', $node_depth) . '</li>';

        $result .= '</ul>';

        return $result;
    }

用法:

$categories = array(
    array('name' => '1. item',
        'depth' => '0',
        'lft' => '1',
        'rgt' => '2'),
    array('name' => '2. item',
        'depth' => '0',
        'lft' => '3',
        'rgt' => '22'),
    array('name' => '2.1 item',
        'depth' => '1',
        'lft' => '4',
        'rgt' => '5'),
    array('name' => '2.2 item',
        'depth' => '1',
        'lft' => '6',
        'rgt' => '13'),
    array('name' => '2.2.1 item',
        'depth' => '2',
        'lft' => '7',
        'rgt' => '8'),
    array('name' => '2.2.2 item',
        'depth' => '2',
        'lft' => '9',
        'rgt' => '10'),
    array('name' => '2.2.3 item',
        'depth' => '2',
        'lft' => '11',
        'rgt' => '12'),
    array('name' => '2.3 item',
        'depth' => '1',
        'lft' => '14',
        'rgt' => '15'),
    array('name' => '2.4 item',
        'depth' => '1',
        'lft' => '16',
        'rgt' => '21'),
    array('name' => '2.4.1 item',
        'depth' => '2',
        'lft' => '17',
        'rgt' => '18'),
    array('name' => '2.4.2 item',
        'depth' => '2',
        'lft' => '19',
        'rgt' => '20'),
    array('name' => '3. item',
        'depth' => '0',
        'lft' => '23',
        'rgt' => '24'),
    array('name' => '4. item',
        'depth' => '0',
        'lft' => '25',
        'rgt' => '34'),
     array('name' => '4.1 item',
        'depth' => '1',
        'lft' => '26',
        'rgt' => '27'),
     array('name' => '4.2 item',
        'depth' => '1',
        'lft' => '28',
        'rgt' => '33'),
     array('name' => '4.2.1 item',
        'depth' => '2',
        'lft' => '29',
        'rgt' => '30'),
     array('name' => '4.2.2 item',
        'depth' => '2',
        'lft' => '31',
        'rgt' => '32',
         'category_id' => 5),
    array('name' => '5. item',
        'depth' => '0',
        'lft' => '35',
        'rgt' => '36'),
);
$current = array('lft' => '9', 'rgt' => '10');
print MyRenderTree($categories, $current);

The function expect the $tree is order by the 'left'.

I have modified your function to selected items based on the 'left' and 'right' value. Hope it's what you are after.

The modified function:

function MyRenderTree($tree = array(array('name' => '', 'depth' => '', 'lft' => '', 'rgt' => '')), $current=false)
    {
        $current_depth = 0;
        $counter = 0;
        $found = false;
        $nextSibling = false;
        $result = '<ul>';
        foreach ($tree as $node) {
            $node_depth = $node['depth'];
            $node_name = $node['name'];
            $node_id = 1;//$node['category_id'];

            if ($current !== false) {

                if ($node_depth ==0) {

                    if ($node['lft'] <= $current['lft'] && $node['rgt'] >= $current['rgt']) {
                        // 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['lft'] <= $node['lft'] && $root['rgt'] >= $node['rgt']);
                    if (!$isInRange) {
                        // skip all of the items that are not in range of the selected root
                        continue;
                    } else if (isset($current['lft']) && $node['lft'] == $current['lft']) {
                        // selected item reached
                        $found  = true;
                        $current = $node;
                    } else if ($nextSibling !== false && $nextSibling['depth'] < $node['depth']) {

                        // 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_depth == $node['depth']) {
                        // siblings after the selected item
                        $nextSibling = $node;
                    }
                }
            } else if ($node_depth > 0) {
                // show root items only if no childern is selected
                continue;
            }

            if ($node_depth == $current_depth) {
                if ($counter > 0)
                    $result .= '</li>';
            }
            elseif ($node_depth > $current_depth) {

                $result .= '<ul>';
                $current_depth = $current_depth + ($node_depth - $current_depth);
            } elseif ($node_depth < $current_depth) {

                $result .= str_repeat('</li></ul>', $current_depth - $node_depth) . '</li>';
                $current_depth = $current_depth - ($current_depth - $node_depth);
            }
            $result .= '<li id="c' . $node_id . '" ';
            $result .= $node_depth < 2 ?' class="open"':'';
            $result .= '><a href="#">' . $node_name .'(' . $node['lft'] . '-' . $node['rgt'] . ')' . '</a>';
            ++$counter;
        }
        unset($found);
        unset($nextSibling);

        $result .= str_repeat('</li></ul>', $node_depth) . '</li>';

        $result .= '</ul>';

        return $result;
    }

Usage:

$categories = array(
    array('name' => '1. item',
        'depth' => '0',
        'lft' => '1',
        'rgt' => '2'),
    array('name' => '2. item',
        'depth' => '0',
        'lft' => '3',
        'rgt' => '22'),
    array('name' => '2.1 item',
        'depth' => '1',
        'lft' => '4',
        'rgt' => '5'),
    array('name' => '2.2 item',
        'depth' => '1',
        'lft' => '6',
        'rgt' => '13'),
    array('name' => '2.2.1 item',
        'depth' => '2',
        'lft' => '7',
        'rgt' => '8'),
    array('name' => '2.2.2 item',
        'depth' => '2',
        'lft' => '9',
        'rgt' => '10'),
    array('name' => '2.2.3 item',
        'depth' => '2',
        'lft' => '11',
        'rgt' => '12'),
    array('name' => '2.3 item',
        'depth' => '1',
        'lft' => '14',
        'rgt' => '15'),
    array('name' => '2.4 item',
        'depth' => '1',
        'lft' => '16',
        'rgt' => '21'),
    array('name' => '2.4.1 item',
        'depth' => '2',
        'lft' => '17',
        'rgt' => '18'),
    array('name' => '2.4.2 item',
        'depth' => '2',
        'lft' => '19',
        'rgt' => '20'),
    array('name' => '3. item',
        'depth' => '0',
        'lft' => '23',
        'rgt' => '24'),
    array('name' => '4. item',
        'depth' => '0',
        'lft' => '25',
        'rgt' => '34'),
     array('name' => '4.1 item',
        'depth' => '1',
        'lft' => '26',
        'rgt' => '27'),
     array('name' => '4.2 item',
        'depth' => '1',
        'lft' => '28',
        'rgt' => '33'),
     array('name' => '4.2.1 item',
        'depth' => '2',
        'lft' => '29',
        'rgt' => '30'),
     array('name' => '4.2.2 item',
        'depth' => '2',
        'lft' => '31',
        'rgt' => '32',
         'category_id' => 5),
    array('name' => '5. item',
        'depth' => '0',
        'lft' => '35',
        'rgt' => '36'),
);
$current = array('lft' => '9', 'rgt' => '10');
print MyRenderTree($categories, $current);
三生池水覆流年 2024-12-16 04:09:55

http://www.jstree.com/ 是一个 jQuery 插件,它将为您更优雅地处理这个问题比尝试基于 PHP 的解决方案更快。

查看 http://www.jstree.com/demo 获取现场演示以及有关 Tom 如何实施的说明。

http://www.jstree.com/ is a jQuery plugin which will handle this for you far more elegantly and quickly than trying to do a PHP based solution.

Check out http://www.jstree.com/demo for a live demo and instruction on how tom implement.

自找没趣 2024-12-16 04:09:55

基于 satrun77 的回答。我为 + + (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;
}

额外标签: ,

Based on answer by satrun77. I created a helper for + + (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: ,

御守 2024-12-16 04:09:55

此方法检查该节点是否是所选节点的父节点、所选节点或深度=0。只有满足这些条件之一的节点的迭代才会将列表项添加到结果字符串中。所有节点都会获得选定的班级、开放班级或两者。否则,它就是你的代码。

$current_depth = 0;
$counter = 0;

$result = '<ul>';

foreach($tree as $node){
   $node_depth = $node['depth'];
   $node_name = $node['name'];
   $node_id = $node['category_id'];
   $selected = false; 

   if( $node['lft'] <= current['lft'] && $node['rgt'] >= $current['rgt'] ) $selected=true

   if ($node_depth == 0 || $selected == true)
   {
     if($node_depth == $current_depth)
     {
       if($counter > 0) $result .= '</li>';
     }
     elseif($node_depth > $current_depth)
     {
       $result .= '<ul>';
       $current_depth = $current_depth + ($node_depth - $current_depth);
     }
     elseif($node_depth < $current_depth)
     {
       $result .= str_repeat('</li></ul>',$current_depth - $node_depth).'</li>';
       $current_depth = $current_depth - ($current_depth - $node_depth);
     }

     $result .= '<li id="c'.$node_id.'"';
     $result .= ' class="';
     $result .= $node_depth < 2 ?' open':' ';
     $result .= $select == true  ?' selected':' ';
     $result .= '"';
     $result .= '><a href="#">'.$node_name.'</a>';
     ++$counter;
   }
}


$result .= str_repeat('</li></ul>',$node_depth).'</li>';

  $result .= '</ul>';

  return $result;
}

// “$current”可能包含活动列表项的category_id、lft、rgt
打印 MyRenderTree($categories,$current);
?>

This method checks to see if the node is a parent of the selected node, the selected node, or depth=0. Only iterations for nodes which meet one of these conditions add list items to the result string. All of the nodes get either the selected class, open class or both. Otherwise, it is your code.

$current_depth = 0;
$counter = 0;

$result = '<ul>';

foreach($tree as $node){
   $node_depth = $node['depth'];
   $node_name = $node['name'];
   $node_id = $node['category_id'];
   $selected = false; 

   if( $node['lft'] <= current['lft'] && $node['rgt'] >= $current['rgt'] ) $selected=true

   if ($node_depth == 0 || $selected == true)
   {
     if($node_depth == $current_depth)
     {
       if($counter > 0) $result .= '</li>';
     }
     elseif($node_depth > $current_depth)
     {
       $result .= '<ul>';
       $current_depth = $current_depth + ($node_depth - $current_depth);
     }
     elseif($node_depth < $current_depth)
     {
       $result .= str_repeat('</li></ul>',$current_depth - $node_depth).'</li>';
       $current_depth = $current_depth - ($current_depth - $node_depth);
     }

     $result .= '<li id="c'.$node_id.'"';
     $result .= ' class="';
     $result .= $node_depth < 2 ?' open':' ';
     $result .= $select == true  ?' selected':' ';
     $result .= '"';
     $result .= '><a href="#">'.$node_name.'</a>';
     ++$counter;
   }
}


$result .= str_repeat('</li></ul>',$node_depth).'</li>';

  $result .= '</ul>';

  return $result;
}

// "$current" may contain category_id, lft, rgt for active list item
print MyRenderTree($categories,$current);
?>

春花秋月 2024-12-16 04:09:55

只是想提供一个 OOP、更干净的版本,这应该可以更轻松地添加除所选逻辑之外的任何类型的逻辑。

它可以与@satrun77 发布的数组结构一起正常工作。

class Node
{
    var $name;
    var $category;
    var $depth;
    var $lft;
    var $rgt;
    var $selected;
    var $nodes = array();

    public function __construct( $name, $category, $depth, $lft, $rgt, $selected = false )
    {
        $this->name = $name;
        $this->category = $category;
        $this->depth = $depth;
        $this->lft = $lft;
        $this->rgt = $rgt;
        $this->selected = $selected;
    }

    public function addNode( Node $node )
    {
        array_push( $this->nodes, $node );
    }

    public function render()
    {
        $renderedNodes = '';
        if ( $this->isSelected() ) {
            $renderedNodes = $this->renderNodes();
        }
        return sprintf( '<li id="c%s"><a href="">%s</a>%s</li>', $this->category, $this->name, $renderedNodes );
    }

    protected function renderNodes()
    {
        $renderedNodes = '';
        foreach ( $this->nodes as $node )
        {
            $renderedNodes .= $node->render();
        }
        return sprintf( '<ul>%s</ul>', $renderedNodes );
    }

    /** Return TRUE if this node or any subnode is selected */
    protected function isSelected()
    {
        return ( $this->selected || $this->hasSelectedNode() );
    }

    /** Return TRUE if a subnode is selected */
    protected function hasSelectedNode()
    {
        foreach ( $this->nodes as $node )
        {
            if ( $node->isSelected() )
            {
                return TRUE;
            }
        }
        return FALSE;
    }
}

class RootNode extends Node
{
    public function __construct() {}

    public function render()
    {
        return $this->renderNodes();
    }
}

function MyRenderTree( $tree, $current )
{
    /** Convert the $tree array to a real tree structure based on the Node class */
    $nodeStack = array();
    $rootNode = new RootNode();
    $nodeStack[-1] = $rootNode;

    foreach ( $tree as $category => $rawNode )
    {
        $node = new Node( $rawNode['name'], $category, $rawNode['depth'], $rawNode['lft'], $rawNode['rgt'], $rawNode['lft'] == $current['lft'] );
        $nodeStack[($node->depth -1)]->addNode( $node );
        $nodeStack[$node->depth] = $node;
        end( $nodeStack );
    }

    /** Render the tree and return the output */
    return $rootNode->render();
}

Just wanted to provide a OOP, cleaner version, which should make it easier to add any sort of logic apart from the selected one.

It works properly with the array structure posted by @satrun77.

class Node
{
    var $name;
    var $category;
    var $depth;
    var $lft;
    var $rgt;
    var $selected;
    var $nodes = array();

    public function __construct( $name, $category, $depth, $lft, $rgt, $selected = false )
    {
        $this->name = $name;
        $this->category = $category;
        $this->depth = $depth;
        $this->lft = $lft;
        $this->rgt = $rgt;
        $this->selected = $selected;
    }

    public function addNode( Node $node )
    {
        array_push( $this->nodes, $node );
    }

    public function render()
    {
        $renderedNodes = '';
        if ( $this->isSelected() ) {
            $renderedNodes = $this->renderNodes();
        }
        return sprintf( '<li id="c%s"><a href="">%s</a>%s</li>', $this->category, $this->name, $renderedNodes );
    }

    protected function renderNodes()
    {
        $renderedNodes = '';
        foreach ( $this->nodes as $node )
        {
            $renderedNodes .= $node->render();
        }
        return sprintf( '<ul>%s</ul>', $renderedNodes );
    }

    /** Return TRUE if this node or any subnode is selected */
    protected function isSelected()
    {
        return ( $this->selected || $this->hasSelectedNode() );
    }

    /** Return TRUE if a subnode is selected */
    protected function hasSelectedNode()
    {
        foreach ( $this->nodes as $node )
        {
            if ( $node->isSelected() )
            {
                return TRUE;
            }
        }
        return FALSE;
    }
}

class RootNode extends Node
{
    public function __construct() {}

    public function render()
    {
        return $this->renderNodes();
    }
}

function MyRenderTree( $tree, $current )
{
    /** Convert the $tree array to a real tree structure based on the Node class */
    $nodeStack = array();
    $rootNode = new RootNode();
    $nodeStack[-1] = $rootNode;

    foreach ( $tree as $category => $rawNode )
    {
        $node = new Node( $rawNode['name'], $category, $rawNode['depth'], $rawNode['lft'], $rawNode['rgt'], $rawNode['lft'] == $current['lft'] );
        $nodeStack[($node->depth -1)]->addNode( $node );
        $nodeStack[$node->depth] = $node;
        end( $nodeStack );
    }

    /** Render the tree and return the output */
    return $rootNode->render();
}
素食主义者 2024-12-16 04:09:55

这不是最好的解决方案吗?为什么有这么多类、对象等等?
这个简单的功能在各方面都是完美且灵活的。
演示

$categories = array(
array('id'=>1,'name'=>'test1','parent'=>0),
array('id'=>2,'name'=>'test2','parent'=>0),
array('id'=>3,'name'=>'test3','parent'=>1),
array('id'=>4,'name'=>'test4','parent'=>2),
array('id'=>5,'name'=>'test5','parent'=>1),
array('id'=>6,'name'=>'test6','parent'=>4),
array('id'=>7,'name'=>'test7','parent'=>6),
array('id'=>8,'name'=>'test7','parent'=>3)
); 
$cats = array();
foreach($categories as &$category)
    $cats[$category['parent']][] = $category;
unset($categories);

$selected = 6; // selected id;
echo standartCategory($cats,$selected);
function standartCategory(&$categories,$selected = '',$parent = 0 /*MAIN CATEGORY*/)
{
    if (!isset($categories[$parent])) return array('',0);
    $html = '';
    $haveSelected = 0;
    foreach($categories[$parent] as $category) {

        list($childHtml,$isVisible)   = standartCategory($categories,$selected,$category["id"]);

        $isSelected = $category['id']===$selected;
        if (! ($isVisible | $isSelected)) { // this if to prevent output
            $html .= '<li>'.$category['name'].'</li>';
            continue;
        }

        $haveSelected |= $isVisible | $isSelected;

        $html  .= '<li>'.$category['name'].$childHtml.'</li>';
    }

    return  $parent ? array('<ul>'.$html.'</ul>',$haveSelected) : '<ul>'.$html.'</ul>';
}

isn't it the best solution. why there are so many classes, objects bla bla.. ?
this simple function is perfect and flexible in everyways.
DEMO

$categories = array(
array('id'=>1,'name'=>'test1','parent'=>0),
array('id'=>2,'name'=>'test2','parent'=>0),
array('id'=>3,'name'=>'test3','parent'=>1),
array('id'=>4,'name'=>'test4','parent'=>2),
array('id'=>5,'name'=>'test5','parent'=>1),
array('id'=>6,'name'=>'test6','parent'=>4),
array('id'=>7,'name'=>'test7','parent'=>6),
array('id'=>8,'name'=>'test7','parent'=>3)
); 
$cats = array();
foreach($categories as &$category)
    $cats[$category['parent']][] = $category;
unset($categories);

$selected = 6; // selected id;
echo standartCategory($cats,$selected);
function standartCategory(&$categories,$selected = '',$parent = 0 /*MAIN CATEGORY*/)
{
    if (!isset($categories[$parent])) return array('',0);
    $html = '';
    $haveSelected = 0;
    foreach($categories[$parent] as $category) {

        list($childHtml,$isVisible)   = standartCategory($categories,$selected,$category["id"]);

        $isSelected = $category['id']===$selected;
        if (! ($isVisible | $isSelected)) { // this if to prevent output
            $html .= '<li>'.$category['name'].'</li>';
            continue;
        }

        $haveSelected |= $isVisible | $isSelected;

        $html  .= '<li>'.$category['name'].$childHtml.'</li>';
    }

    return  $parent ? array('<ul>'.$html.'</ul>',$haveSelected) : '<ul>'.$html.'</ul>';
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文