2011-12-21 11 views
63

Sto cercando di scrivere una funzione che accetta una matrice di pagine/categorie (da un risultato di database flat) e genera una serie di elementi di pagina/categoria nidificati basati sugli ID padre . Mi piacerebbe farlo in modo ricorsivo, in modo da poter eseguire qualsiasi livello di nidificazione.Funzione ricorsiva per generare array multidimensionale dal risultato del database

Per esempio: sto recupero tutte le pagine in una query, e questa è la cosa la tabella del database si presenta come

+-------+---------------+---------------------------+ 
| id | parent_id |   title   | 
+-------+---------------+---------------------------+ 
| 1 |  0  | Parent Page    | 
| 2 |  1  | Sub Page    | 
| 3 |  2  | Sub Sub Page   | 
| 4 |  0  | Another Parent Page  | 
+-------+---------------+---------------------------+ 

E questa è la matrice Vorrei finire con per elaborare in i miei file di visualizzazione:

Array 
(
    [0] => Array 
     (
      [id] => 1 
      [parent_id] => 0 
      [title] => Parent Page 
      [children] => Array 
         (
          [0] => Array 
           (
            [id] => 2 
            [parent_id] => 1 
            [title] => Sub Page 
            [children] => Array 
               (
                [0] => Array 
                 (
                  [id] => 3 
                  [parent_id] => 1 
                  [title] => Sub Sub Page 
                 ) 
               ) 
           ) 
         ) 
     ) 
    [1] => Array 
     (
      [id] => 4 
      [parent_id] => 0 
      [title] => Another Parent Page 
     ) 
) 

ho guardato e ha cercato quasi ogni soluzione che ho incontrato (ci sono un sacco di loro qui su Stack Overflow, ma non hanno avuto fortuna ottenere qualcosa di abbastanza generico che funziona per entrambi pagine e categorie

Ecco il più vicino che ho ottenuto, ma non funziona perché sto assegnando i figli al genitore di primo livello.

function page_walk($array, $parent_id = FALSE) 
{ 
    $organized_pages = array(); 

    $children = array(); 

    foreach($array as $index => $page) 
    { 
     if ($page['parent_id'] == 0) // No, just spit it out and you're done 
     { 
      $organized_pages[$index] = $page; 
     } 
     else // If it does, 
     {  
      $organized_pages[$parent_id]['children'][$page['id']] = $this->page_walk($page, $parent_id); 
     } 
    } 

    return $organized_pages; 
} 

function page_list($array) 
{  
    $fakepages = array(); 
    $fakepages[0] = array('id' => 1, 'parent_id' => 0, 'title' => 'Parent Page'); 
    $fakepages[1] = array('id' => 2, 'parent_id' => 1, 'title' => 'Sub Page'); 
    $fakepages[2] = array('id' => 3, 'parent_id' => 2, 'title' => 'Sub Sub Page'); 
    $fakepages[3] = array('id' => 4, 'parent_id' => 3, 'title' => 'Another Parent Page'); 

    $pages = $this->page_walk($fakepages, 0); 

    print_r($pages); 
} 
+1

Non puoi semplicemente lavorare con una serie di tutte le parent_ids e un altro array per le tue pagine? – djot

risposta

169

Alcuni molto semplice, generico edificio albero:

function buildTree(array $elements, $parentId = 0) { 
    $branch = array(); 

    foreach ($elements as $element) { 
     if ($element['parent_id'] == $parentId) { 
      $children = buildTree($elements, $element['id']); 
      if ($children) { 
       $element['children'] = $children; 
      } 
      $branch[] = $element; 
     } 
    } 

    return $branch; 
} 

$tree = buildTree($rows); 

L'algoritmo è abbastanza semplice:

  1. Prendere l'array di tutti gli elementi e l'id del genitore corrente (inizialmente 0/niente/null/qualunque).
  2. Passa attraverso tutti gli elementi.
  3. Se lo parent_id di un elemento corrisponde all'ID genitore corrente ottenuto in 1., l'elemento è figlio del genitore. Inseriscilo nella lista dei bambini attuali (qui: $branch).
  4. Chiama la funzione in modo ricorsivo con l'id dell'elemento appena identificato in 3., quindi trova tutti i figli di quell'elemento e aggiungili come elemento children.
  5. Restituisci l'elenco dei bambini trovati.

In altre parole, un'esecuzione di questa funzione restituisce un elenco di elementi che sono figli dell'id padre corrispondente. Chiamalo con buildTree($myArray, 1), restituirà un elenco di elementi che hanno l'id genitore 1. Inizialmente questa funzione viene chiamata con l'id genitore uguale a 0, quindi vengono restituiti elementi senza id padre, che sono nodi radice. La funzione si chiama in modo ricorsivo per trovare figli di bambini.

+0

Funziona perfettamente. Riesco a vedere dove ho sbagliato nella logica della mia versione. Modo di rock it! Non ho avuto bisogno di usare la ricorsione molto spesso nei progetti, quindi questo sicuramente aiuta un sacco. –

+1

Felice è d'aiuto. Da notare: questo è alquanto inefficiente dal momento che passa sempre l'intera matrice '$ elements' verso il basso. Per i piccoli array che non ha importanza, ma per i set di dati di grandi dimensioni, è necessario rimuovere l'elemento già abbinato da esso prima di inviarlo. Però diventa un po 'complicato, quindi l'ho lasciato semplice per la tua comprensione. :) – deceze

+5

@deceze Vorrei vedere anche la versione disordinata. Grazie in anticipo! –

0

È possibile utilizzare php per ottenere il risultato mysql in array e quindi utilizzarlo.

$categoryArr = Array(); 
while($categoryRow = mysql_fetch_array($category_query_result)){ 
    $categoryArr[] = array('parentid'=>$categoryRow['parent_id'], 
      'id'=>$categoryRow['id']); 
    } 
9

So che questa domanda è vecchio, ma stavo affrontando un problema molto simile - se non con una grande quantità di dati. Dopo un po 'di difficoltà, sono riuscito a costruire l'albero in un unico passaggio del set di risultati, usando i riferimenti. Questo codice non è carino, ma funziona e funziona abbastanza velocemente.È non ricorsivo - cioè, c'è solo un passaggio sulla resultset e poi uno array_filter alla fine:

$dbh = new PDO(CONNECT_STRING, USERNAME, PASSWORD); 
$dbs = $dbh->query("SELECT n_id, n_parent_id from test_table order by n_parent_id, n_id"); 
$elems = array(); 

while(($row = $dbs->fetch(PDO::FETCH_ASSOC)) !== FALSE) { 
    $row['children'] = array(); 
    $vn = "row" . $row['n_id']; 
    ${$vn} = $row; 
    if(!is_null($row['n_parent_id'])) { 
     $vp = "parent" . $row['n_parent_id']; 
     if(isset($data[$row['n_parent_id']])) { 
      ${$vp} = $data[$row['n_parent_id']]; 
     } 
     else { 
      ${$vp} = array('n_id' => $row['n_parent_id'], 'n_parent_id' => null, 'children' => array()); 
      $data[$row['n_parent_id']] = &${$vp}; 
     } 
     ${$vp}['children'][] = &${$vn}; 
     $data[$row['n_parent_id']] = ${$vp}; 
    } 
    $data[$row['n_id']] = &${$vn}; 
} 
$dbs->closeCursor(); 

$result = array_filter($data, function($elem) { return is_null($elem['n_parent_id']); }); 
print_r($result); 

Quando eseguito su questi dati:

mysql> select * from test_table; 
+------+-------------+ 
| n_id | n_parent_id | 
+------+-------------+ 
| 1 |  NULL | 
| 2 |  NULL | 
| 3 |   1 | 
| 4 |   1 | 
| 5 |   2 | 
| 6 |   2 | 
| 7 |   5 | 
| 8 |   5 | 
+------+-------------+ 

L'ultimo print_r produce questo output:

Array 
(
    [1] => Array 
     (
      [n_id] => 1 
      [n_parent_id] => 
      [children] => Array 
       (
        [3] => Array 
         (
          [n_id] => 3 
          [n_parent_id] => 1 
          [children] => Array 
           (
           ) 

         ) 

        [4] => Array 
         (
          [n_id] => 4 
          [n_parent_id] => 1 
          [children] => Array 
           (
           ) 

         ) 

       ) 

     ) 

    [2] => Array 
     (
      [n_id] => 2 
      [n_parent_id] => 
      [children] => Array 
       (
        [5] => Array 
         (
          [n_id] => 5 
          [n_parent_id] => 2 
          [children] => Array 
           (
            [7] => Array 
             (
              [n_id] => 7 
              [n_parent_id] => 5 
              [children] => Array 
               (
               ) 

             ) 

            [8] => Array 
             (
              [n_id] => 8 
              [n_parent_id] => 5 
              [children] => Array 
               (
               ) 

             ) 

           ) 

         ) 

        [6] => Array 
         (
          [n_id] => 6 
          [n_parent_id] => 2 
          [children] => Array 
           (
           ) 

         ) 

       ) 

     ) 

) 

Quale è esattamente quello che stavo cercando.

+0

Sei incredibile. Questo funziona perfettamente per me. Lo stavo cercando dagli ultimi 2 giorni. Questa risposta dovrebbe essere contrassegnata come migliore risposta. – Swadesh

+0

mentre la soluzione è intelligente, ma questo codice ha un bug, mi ha dato risultati diversi in diverse situazioni – Mohammadhzp

+0

@Mhammadhzp Ho usato questa soluzione in produzione per l'ultimo anno e non ho avuto problemi con esso.Se i vostri dati sono diversi, otterrete risultati diversi :) –

0

Per grandi array: matrice

passaggio per riferimento.

function buildTree(&$elements, $parentId = 0) { //pass main array by reference 
    $branch = array(); 

    foreach ($elements as $key => $element) { 
     if ($element['parent_id'] == $parentId) { 

      $element['children'] = buildTree($elements, $element['id']); 

      $branch[] = $element;     
     } 
    } 
    return $branch; 
} 

$tree = buildTree($rows); 

Se si passa gamma sia di riferimento allora stesso array utilizzerà in ogni funzione ricorsiva non c'è bisogno di unire le matrici figlio genitore a fine

Problemi correlati