RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 949390
Accepted
Roman Andreev
Roman Andreev
Asked:2020-02-26 03:29:13 +0000 UTC2020-02-26 03:29:13 +0000 UTC 2020-02-26 03:29:13 +0000 UTC

构建一个包含崩溃\剧透的嵌套集合树

  • 772

该数据库有一个嵌套集数据结构和一个使用 yii2/ 的项目,您需要将此结构显示为嵌套Collapse。

这是按 RGT 排序的结构的样子: 按 rgt 排序的嵌套集

结果应如下所示:

-*spoiler*  
--*spoiler*  
--*spoiler*  
----*spoiler*  
-*spoiler*  
--*spoiler*   
----*spoiler*  
-*spoiler*   
-*spoiler*   
-*spoiler*   
-*spoiler*   
-*spoiler*   
-*spoiler*  
-*spoiler*  
--*spoiler*   
-*spoiler* 

添加扰流板如下。这是一个 yii2 小部件,它返回扰流板的布局,已经应用了样式并放置id了将执行 js-fold-expand 的样式:

private static function createCollapse($label, $content)
{
    return Collapse::widget(
        [
            'items' => [
                [
                    //В таблице поле LABEL
                    'label' => $label,
                    //В качестве контента ID записи
                    'content' => $content
                ]
            ]
        ]
    );
}

布局看起来像这样:

<div class="panel panel-default">
    <div class="panel-heading">
        <h4 class="panel-title"><a class="collapse-toggle collapsed" href="#w7-collapse7" data-toggle="collapse" data-parent="#w7" aria-expanded="false">Main Spoiler</a></h4>
    </div>
    <div id="w7-collapse7" class="panel-collapse collapse" aria-expanded="false" style="height: 0px;">
        <div class="panel-body">
            <div id="w6" class="panel-group collapse in" aria-expanded="true" style="">
                <div class="panel panel-default">
                    <div class="panel-heading">
                        <h4 class="panel-title"><a class="collapse-toggle collapsed" href="#w6-collapse1" data-toggle="collapse" data-parent="#w6" aria-expanded="false">Sub spoiler</a></h4>
                    </div>
                    <div id="w6-collapse1" class="panel-collapse collapse" aria-expanded="false" style="height: 0px;">
                        <div class="panel-body">
                            content
                        </div>
                    </div>
                </div>
                <div class="panel panel-default">
                    <div class="panel-heading">
                        <h4 class="panel-title"><a class="collapse-toggle collapsed" href="#w6-collapse2" data-toggle="collapse" data-parent="#w6" aria-expanded="false">Sub Spoiler</a></h4>
                    </div>
                    <div id="w6-collapse2" class="panel-collapse collapse" aria-expanded="false" style="height: 0px;">
                        <div class="panel-body">
                            content
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

据我了解,简单地从根目录遍历所有子项是行不通的,因为每个顶级扰流器小部件都必须已经知道它的内容,而这些内容又可以包含无限数量的相同扰流器小部件,依此类推。 .

一种解决方案是在一次查询中获取按 RGT 排序的表,并检查当前 LVL 的 3 种情况 - 大于\小于\等于 -使用<ul><li>. 但是我不能以任何方式调整这个例子来使用一个小部件:(

第二个选项,正如@fedornabilkin 所建议的那样,但nested sets不要自行存储parent id,而且再次包装有困难Collapse::widget

我会很高兴有任何帮助!

php
  • 2 2 个回答
  • 10 Views

2 个回答

  • Voted
  1. fedornabilkin
    2020-02-26T17:40:51Z2020-02-26T17:40:51Z

    我认为有必要通过一个请求接收所有数据。然后准备一个多维数组,在其中分解每个父级的子级。

    $cats = [];
    foreach($rows as $model){
        $cats[$model->parent][] = $model;
    }
    

    然后将这个数组放入递归方法中。像这样以树的形式构建列表。

    public static function createTree($cats, $parent)
    {
        if(isset($cats[$parent]) && is_array($cats[$parent])) {
            $tree = '<ul>';
            foreach ($cats[$parent] as $model) {
                $tree .= '<li>' . $model->title;
                $tree .= self::createTree($cats, $model->id);
                $tree .= '</li>';
            }
            $tree .= '</ul>';
        }
        else{
            return null;
        }
        return $tree;
    }
    

    在视图中我们调用方法echo className::createTree($cats, 1);

    UPD
    为清楚起见,您可以运行示例代码:

    $rows = [];
    $rows[] = ['id' => 1, 'title' => 'title 1', 'parent' => 0];
    $rows[] = ['id' => 2, 'title' => 'title 2', 'parent' => 0];
    $rows[] = ['id' => 3, 'title' => 'title 1 1', 'parent' => 1];
    $rows[] = ['id' => 4, 'title' => 'title 1 2', 'parent' => 1];
    $rows[] = ['id' => 5, 'title' => 'title 1 2 1', 'parent' => 4];
    $rows[] = ['id' => 6, 'title' => 'title 1 2 2', 'parent' => 4];
    $rows[] = ['id' => 7, 'title' => 'title 3', 'parent' => 0];
    $rows[] = ['id' => 8, 'title' => 'title 3 1', 'parent' => 7];
    $rows[] = ['id' => 9, 'title' => 'title 3 2', 'parent' => 7];
    
    foreach($rows as $model){
        $cats[$model['parent']][] = $model;
    }
    
    function createTree($cats, $parent)
    {
        if(isset($cats[$parent]) && is_array($cats[$parent])) {
            $tree = '<ul>';
            foreach ($cats[$parent] as $model) {
                $tree .= '<li>' . $model['title'];
                $tree .= createTree($cats, $model['id']);
                $tree .= '</li>';
            }
            $tree .= '</ul>';
        }
        else{
            return null;
        }
        return $tree;
    }
    
    echo createTree($cats, 0);
    

    在此处输入图像描述

    • 2
  2. Best Answer
    Roman Andreev
    2020-03-03T16:42:44Z2020-03-03T16:42:44Z

    因此,在稍微处理了递归nested sets之后,编写了以下解决方案:

    1. 我们使用数据库中的一个查询来绘制树:

      SELECT * FROM table WHERE ROOT = $root ORDER BY lft

    2. 接下来,让我们创建辅助函数来搜索后代并通过以下方式过滤树LVL:

      /**
       * Выделяет из дерева $tree потомков узла $node
       *
       * @param Tree[] $tree Массив узлов дерева для фильтрации
       * @param Tree $node Узел дерева потомки которого будут возвращены
       * @return Tree[]|[]
       */
      public static function filterChildren(array $tree, Tree $node)
      {
          return array_filter(
              $tree,
              function ($element) use ($node) {
                  return $element->LFT > $node->LFT
                      && $element->RGT < $node->RGT
                      && $element->ROOT === $node->ROOT;
              }
          );
      }
      
      /**
      * Фильтрация дерева по параметру LVL;
      * Результирующий массив будет содержать только узлы с уровнем `$lvl`
      *
      * @param Tree[] $tree Массив узлов дерева для фильтрации
      * @param int $lvl Уровень по которому осущесвляется фильтрация
      * @return Tree[]
      */
      public static function filterByLvl(array $tree, $lvl)
      {
          return array_filter(
              $tree,
              function ($element) use ($lvl) {
                  return $element->LVL === $lvl;
              }
          );
      }
      
    3. 渲染折叠的方法,其实是为了减少代码:

      private static function createCollapse(Tree $node, $content)
      {
          return Collapse::widget(
              [
                  'items' => [
                      [
                          'label' => $node->NAME,
                          'content' => $content
                      ]
                  ]
              ]
          );
      }
      
    4. 递归函数本身将如下所示:

      /**
      * Рендеринг дерева в виде спойлеров.
      *
      * Метод возвращает HTML верстку дерева выполненную в виде вложенных друг в друга спойлеров.
      *
      * @param array $roots Массив узлов дерева для которых необходимо построить спойлеры
      * @param array $fullTree Полное дерево, включая детей и предков всех элементов
      *
      * @return string
      */
      public static function renderAsCollapses(array $roots, array $fullTree)
      {
          $collapses =  '';
          foreach ($roots as $key =>  $node) {
              /** @var Tree $node */
              //Проверка на наличие потомков
              if ($node->RGT - $node->LFT > 1) {
                  $collapses .= self::createCollapse(
                      $node,
                      self::renderAsCollapses(
                          ClassifierTree::filterChildren($fullTree, $node),
                          $fullTree,
                      )
                  );
              } else {
                  $collapses .= self::createCollapse(
                      $node,
                      'content'
                  );
              }
          }
          return $collapses;
      }
      

    用法:

        //$tree содержит массив элементов дерева, полученный из БД с помощью запроса
        //SELECT * FROM table WHERE ROOT = $root ORDER BY lft 
        $collapses = TreeViewHelper::renderAsCollapses(
            //Для корректной работы функции, первым аргументом необходимо передать
            //массив корневых элементов, для которого будет строится дерево.
            //Начинать построение можно от любого уровня вложенности.
            Tree::filterByLvl($tree, 1),
            $tree,
        );
    

    需要将两个数组传递给函数,这看起来有点奇怪。这样做是为了在第一次调用该函数时,只为根元素绘制扰流板。如果将整棵树传递给函数,则渲染函数通常会针对树的每个节点执行。在这种情况下,在输出中,我们将获得与树中元素一样多的扰流器,而且每个具有后代的元素也将包含嵌套的扰流器。也许这个问题可以用更简洁的方式避免,但不幸的是,我找不到它。如果有人告诉我,我会很高兴。

    唯一让我对这个解决方案感到困惑的是堆栈溢出问题。在堆栈溢出之前嵌套必须有多大?

    • 0

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    根据浏览器窗口的大小调整背景图案的大小

    • 2 个回答
  • Marko Smith

    理解for循环的执行逻辑

    • 1 个回答
  • Marko Smith

    复制动态数组时出错(C++)

    • 1 个回答
  • Marko Smith

    Or and If,elif,else 构造[重复]

    • 1 个回答
  • Marko Smith

    如何构建支持 x64 的 APK

    • 1 个回答
  • Marko Smith

    如何使按钮的输入宽度?

    • 2 个回答
  • Marko Smith

    如何显示对象变量的名称?

    • 3 个回答
  • Marko Smith

    如何循环一个函数?

    • 1 个回答
  • Marko Smith

    LOWORD 宏有什么作用?

    • 2 个回答
  • Marko Smith

    从字符串的开头删除直到并包括一个字符

    • 2 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5