【问题标题】:Adjacency tree from single table单表邻接树
【发布时间】:2009-04-14 15:33:09
【问题描述】:

我读过很多人讨论嵌套列表,但我想知道如何在 PHP 中遍历邻接列表/树。

我有一张桌子,里面有:id、title、parent_id

我已将所有记录选择到一个名为 $pages 的数组中。

然后使用这个php:

function makeList($pages, $used) {
    if (count($pages)) {
        echo "<ul>";
        foreach ($pages as $page) {
            echo "<li>".$page['pag_title'];
            $par_id = $page['pag_id'];
            $subsql("SELECT * FROM pages WHERE pag_parent = ".$par_id."");

            // running the new sql through an abstraction layer
            $childpages = $dbch->fetchAll();
            makeList($childpages, $used, $lastused);
            echo "</li>";
        }
        echo "</ul>";
    }
}

这种工作,但我最终会重复任何子菜单,例如

  • 首页
    • 新闻
      • 子新闻
    • 文章
      • 文章
  • 新闻
    • 子新闻
  • 文章
    • 文章
  • 子新闻
  • 文章

我尝试将当前 id 添加到通过函数传递的数组中,然后使用 in_array 检查它是否存在,但我没有这样做。

任何帮助将不胜感激。

我需要解析整个树,所以不能选择 parent 作为 0

【问题讨论】:

    标签: php tree hierarchy adjacency-list


    【解决方案1】:

    由于它已经执行了 SQL,因此您不必在第一次函数调用之前执行它。

    function makeList($par_id = 0) {
        //your sql code here
        $subsql("SELECT * FROM pages WHERE pag_parent = $par_id");
        $pages = $dbch->fetchAll();
    
        if (count($pages)) {
            echo '<ul>';
            foreach ($pages as $page) {
                echo '<li>', $page['pag_title'];
                makeList($page['pag_id']);
                echo '</li>';
            }
            echo '</ul>';
        }
    }
    

    为了存储更多的树,你可能想看看这个网站:Storing Hierarchical Data in a Database

    【讨论】:

    • @SamDark 是的,我个人会更分层地存储数据。
    【解决方案2】:

    如果您创建一个按父 ID 分组的页面数组,则递归构建列表非常容易。这只需要一个数据库查询。

    <?php
    
     //example data
     $items = array(
        array('id'=>1, 'title'=>'Home', 'parent_id'=>0),
        array('id'=>2, 'title'=>'News', 'parent_id'=>1),
        array('id'=>3, 'title'=>'Sub News', 'parent_id'=>2),
        array('id'=>4, 'title'=>'Articles', 'parent_id'=>0),
        array('id'=>5, 'title'=>'Article', 'parent_id'=>4),
        array('id'=>6, 'title'=>'Article2', 'parent_id'=>4)
     );
    
     //create new list grouped by parent id
     $itemsByParent = array();
     foreach ($items as $item) {
        if (!isset($itemsByParent[$item['parent_id']])) {
            $itemsByParent[$item['parent_id']] = array();
        }
    
        $itemsByParent[$item['parent_id']][] = $item;
     }
    
     //print list recursively 
     function printList($items, $parentId = 0) {
        echo '<ul>';
        foreach ($items[$parentId] as $item) {
            echo '<li>';
            echo $item['title'];
            $curId = $item['id'];
            //if there are children
            if (!empty($items[$curId])) {
                makeList($items, $curId);
            }           
            echo '</li>';
        }
        echo '</ul>';
     }
    
    printList($itemsByParent);
    

    【讨论】:

      【解决方案3】:

      $page 来自哪里?如果您没有转义或使用准备好的语句,您的代码中可能存在 sql 注入漏洞。

      另外,for 循环中的 SELECT 语句作为一种不好的做法跳出。如果表不是那么大,那么选择整个表的内容,然后在 PHP 中遍历结果集,构建树形数据结构。在您的树是链表的病态情况下,这可能需要 n*(n-1)/2 次迭代。当所有节点都添加到树中时停止,或者剩余节点的数量从一次迭代到下一次迭代保持不变 - 这意味着剩余节点不是根节点的子节点。

      或者,如果您的数据库支持递归 SQL 查询,您可以使用它,它只会选择您的父节点的子节点。您仍然需要在 PHP 中自己构建树对象。查询的形式类似于:

      WITH temptable(id, title, parent_id) AS (
        SELECT id, title, parent_id FROM pages WHERE id = ?
        UNION ALL
        SELECT a.id, a.title, a.parent_id FROM pages a, temptable t
         WHERE t.parent_id = a.id
      ) SELECT * FROM temptable
      

      替换“?”在带有起始页 ID 的第二行。

      【讨论】:

      • $page 来自 $pages 数组,它本身来自 sql(sql 选择在单独的类中完成,所有内容都被转义以避免 sql 注入)这是我的 php感兴趣,而不是 SQL,我很好,谢谢
      【解决方案4】:

      最简单的解决方法是,当您进行初始选择以设置 $pages(您没有显示)时,添加一个 WHERE 子句,例如:

      WHERE pag_parent = 0
      

      (或 IS NULL,取决于您存储“顶级”页面的方式)。

      这样一开始你不会选择所有的孩子。

      【讨论】:

        【解决方案5】:

        当该表变大时,递归可能会变得笨拙。我写了一篇关于无递归方法的博文:http://www.alandelevie.com/2008/07/12/recursion-less-storage-of-hierarchical-data-in-a-relational-database/

        【讨论】:

          【解决方案6】:

          查找节点的最高父级、所有父级和所有子级(Tom Haigh 答案的增强):

          <?php
          
           //sample data (can be pulled from mysql)
           $items = array(
              array('id'=>1, 'title'=>'Home', 'parent_id'=>0),
              array('id'=>2, 'title'=>'News', 'parent_id'=>1),
              array('id'=>3, 'title'=>'Sub News', 'parent_id'=>2),
              array('id'=>4, 'title'=>'Articles', 'parent_id'=>0),
              array('id'=>5, 'title'=>'Article', 'parent_id'=>4),
              array('id'=>6, 'title'=>'Article2', 'parent_id'=>4)
           );
          
           //create new list grouped by parent id
           $itemsByParent = array();
           foreach ($items as $item) {
              if (!isset($itemsByParent[$item['parent_id']])) {
                  $itemsByParent[$item['parent_id']] = array();
              }
          
              $itemsByParent[$item['parent_id']][] = $item;
           }
          
           //print list recursively 
           function printList($items, $parentId = 0) {
              echo '<ul>';
              foreach ($items[$parentId] as $item) {
                  echo '<li>';
                  echo $item['title'];
                  $curId = $item['id'];
                  //if there are children
                  if (!empty($items[$curId])) {
                      printList($items, $curId);
                  }           
                  echo '</li>';
              }
              echo '</ul>';
           }
          
          printList($itemsByParent);
          
          
          /***************Extra Functionality 1****************/
          
          function findTopParent($id,$ibp){
          
          
              foreach($ibp as $parentID=>$children){ 
          
                      foreach($children as $child){
          
          
                      if($child['id']==$id){
          
          
                       if($child['parent_id']!=0){
          
                      //echo $child['parent_id'];
                      return findTopParent($child['parent_id'],$ibp);
          
                    }else{ return $child['title'];}
          
                   }              
                  }
          }
          }
          
          $itemID=7;  
          $TopParent= findTopParent($itemID,$itemsByParent);
          
          
          
          
          
          /***************Extra Functionality 2****************/
          
          function getAllParents($id,$ibp){ //full path
          
          foreach($ibp as $parentID=>$nodes){ 
          
              foreach($nodes as $node){
          
                  if($node['id']==$id){
          
                       if($node['parent_id']!=0){
          
                          $a=getAllParents($node['parent_id'],$ibp);
                          array_push($a,$node['parent_id']);
                          return $a;
          
                        }else{
                              return array();
                            }
          
                       }
              }
          }
          }
          
          
          $FullPath= getAllParents(3,$itemsByParent);
          print_r($FullPath);
          
          /*
          Array
          (
          [0] => 1
          [1] => 2
          )
          */
          
          /***************Extra Functionality 3****************/
          
           //this function gets all offspring(subnodes); children, grand children, etc...
           function getAllDescendancy($id,$ibp){
          
           if(array_key_exists($id,$ibp)){
          
                   $kids=array();
                   foreach($ibp[$id] as $child){
          
                      array_push($kids,$child['id']);
          
                      if(array_key_exists($child['id'],$ibp))
          
          $kids=array_merge($kids,getAllDescendancy($child['id'],$ibp));
          
                       }
          
                   return $kids;       
          
               }else{
                      return array();//supplied $id has no kids
                    }
           }
          
          print_r(getAllDescendancy(1,$itemsByParent));
          /*
          Array
          (
          [0] => 2
          [1] => 3
          )
          */
          
          
          print_r(getAllDescendancy(4,$itemsByParent));
          /*
          Array
          (
          [0] => 5
          [1] => 6
          )
          */
          
          
          print_r(getAllDescendancy(0,$itemsByParent));
          /*
          Array
          (
          [0] => 1
          [1] => 2
          [2] => 3
          [3] => 4
          [4] => 5
          [5] => 6
          )
          
          */
          
          ?>
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-07-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-02-08
            相关资源
            最近更新 更多