Tek bir tablodan adjacency ağaç

6 Cevap php

Ben iç içe geçmiş listeleri tartışırken insanların çok okudum, ama ben PHP bir adjacancy liste / ağaç nasıl yinelendiğini merak ediyordum.

Id, başlık, parent_id: Ben bir tablo var

Ve ben $ sayfalar olarak adlandırılan bir diziye üzerinden tüm kayıtları seçtiniz.

Daha sonra bu php kullanarak:

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

Bu eser tür ama herhangi bir alt menüye olmak tekrarlanan, örneğin ile sona

  • Anasayfa
    • Haberler
      • Sub-haber
    • Makaleler
    • Madde
    • Haber
      • Sub-haber
    • Makaleler
    • Madde
      • Alt-haber
      • Makale

      Ben işlevi aracılığıyla iletilir bir diziye geçerli kimliği ekleyerek, ve sonra orada olup olmadığını kontrol etmek in_array'in kullanarak denedim, ama bunu yaparken hiçbir sevinç vardı.

      Herhangi bir yardım çok takdir.

      Ben bu yüzden 0 olarak ebeveyn seçiminde bütün ağaç bir seçenek değildir ayrıştırmak gerekiyor

      6 Cevap

      Zaten SQL yapar beri, sen ilk işlev çağrısından önce dışarıda bunu yapmak zorunda değilsin.

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

      Bu sitede bakmak isteyebilirsiniz gibi ona daha fazla ağaç depolamak için: Storing Hierarchical Data in a Database.

      Eğer ebeveyn id göre gruplandırılmış sayfaları bir dizi oluşturmak eğer ardışık liste oluşturmak oldukça kolaydır. Bu yalnızca bir veritabanı sorgu gerektirir.

      <?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);
      

      Basit düzeltme sadece, $pages (göstermek yok olan) ayarlanmış bir WHERE gibi eklemek için ilk seçme yapıyorsun, olacaktır:

      WHERE pag_parent = 0
      

      (Ya da "üst düzey" sayfaları saklamak konum nasıl bağlı, IS NULL).

      Başlangıçta tüm çocukları seçmek olmaz bu şekilde.

      Nereden $ sayfa geliyor? Bunu kaçan ya da hazırlanmış bir deyimi kullanarak değilseniz sizin kodunda bir SQL injection açığı olabilir.

      Ayrıca döngü için bir iç SELECT deyimi kötü bir uygulama olarak dışarı atlar. Tablo o büyük değilse, o zaman tüm tablonun içeriğini seçin ve ardından ağaç veri yapısını oluşturmak için PHP set sonucu ile yineleme. Bu ağaç bir bağlantılı liste olmanın patolojik durumda (n-1) / 2 tekrarlamalar n * kadar sürebilir. Bütün düğümler ağacına eklenmiş, ya da kalan düğüm sayısı sonraki bir yineleme aynı kalır Dur - Bu kalan düğümler kök düğümün çocukları olmadığı anlamına gelir.

      Veritabanı özyinelemeli SQL sorguları destekler Alternatif olarak, eğer, bunu kullanabilir, ve o sadece ebeveyn düğümün çocukları olan düğümleri seçecektir. Hala ağaç PHP kendinizi nesne oluşturmak zorunda olacak. Sorgu biçimi gibi bir şey olurdu:

      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
      

      Yerine '?' başlangıç ​​sayfası kimliği ile ikinci satırda.

      Bu tablo büyük aldığında, özyineleme hantal alabilirsiniz. Ben bir özyineleme-az yöntemi hakkında bir blog yazısı yazdı: http://www.alandelevie.com/2008/07/12/recursion-less-storage-of-hierarchical-data-in-a-relational-database/

      Iyi ebeveyn, velilerimize ve bir düğüm (Tom Haigh cevabı için geliştirmeleri) tüm çocuklarını bulma:

      <?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
      )
      
      */
      
      ?>