Struttura ad tree PHP per categorie e sottocategorie senza eseguire il ciclo di una query

Sto provando a creare un elenco di categorie con qualsiasi numero di sottocategorie, in cui anche le sottocategorie possono avere le loro sottocategorie.

Ho selezionato tutte le categorie dal mysql db, i gatti sono in un elenco di arrays associati standard, each categoria ha un id, nome, parentid in cui parentid è 0 se è di primo livello.

Fondamentalmente voglio essere in grado di prendere l'arrays a livello singolo di gatti e trasformarlo in una struttura di arrays multidimensionale in cui each categoria può avere un elemento che conterrà un arrays di subcat.

Ora, posso get facilmente questo facendo un ciclo di query per each categoria, ma questo è tutt'altro che ideale, sto cercando di farlo senza colpi extra sul db.

Capisco che ho bisogno di una function ricorsiva per questo. Qualcuno può indicarmi la giusta direzione per questa struttura in stile tree?

Saluti

Questo fa il lavoro:

$items = arrays( (object) arrays('id' => 42, 'parent_id' => 1), (object) arrays('id' => 43, 'parent_id' => 42), (object) arrays('id' => 1, 'parent_id' => 0), ); $childs = arrays(); foreach($items as $item) $childs[$item->parent_id][] = $item; foreach($items as $item) if (isset($childs[$item->id])) $item->childs = $childs[$item->id]; $tree = $childs[0]; print_r($tree); 

Funziona innanzitutto indicizzando le categorie di parent_id. Quindi per each categoria, dobbiamo solo impostare category->childs childs[category->id] , e l'tree è costruito!

Quindi, $tree è l'tree delle categorie. Contiene una matrix di elementi con parent_id = 0, che a loro volta contengono una matrix di loro childs, che a loro volta …

Output di print_r($tree) :

 stdClass Object ( [id] => 1 [parent_id] => 0 [childs] => Array ( [0] => stdClass Object ( [id] => 42 [parent_id] => 1 [childs] => Array ( [0] => stdClass Object ( [id] => 43 [parent_id] => 42 ) ) ) ) ) 

Quindi ecco la function finale:

 function buildTree($items) { $childs = arrays(); foreach($items as $item) $childs[$item->parent_id][] = $item; foreach($items as $item) if (isset($childs[$item->id])) $item->childs = $childs[$item->id]; return $childs[0]; } $tree = buildTree($items); 

Ecco la stessa versione, con gli arrays, che è un po 'complicato in quanto abbiamo bisogno di giocare con i riferimenti (ma funziona altrettanto bene):

 $items = arrays( arrays('id' => 42, 'parent_id' => 1), arrays('id' => 43, 'parent_id' => 42), arrays('id' => 1, 'parent_id' => 0), ); $childs = arrays(); foreach($items as &$item) $childs[$item['parent_id']][] = &$item; unset($item); foreach($items as &$item) if (isset($childs[$item['id']])) $item['childs'] = $childs[$item['id']]; unset($item); $tree = $childs[0]; 

Quindi la versione dell'arrays della function finale:

 function buildTree($items) { $childs = arrays(); foreach($items as &$item) $childs[$item['parent_id']][] = &$item; unset($item); foreach($items as &$item) if (isset($childs[$item['id']])) $item['childs'] = $childs[$item['id']]; return $childs[0]; } $tree = buildTree($items); 

Puoi recuperare tutte le categorie contemporaneamente.

Supponiamo di avere un risultato flat dal database, come questo:

 $categories = arrays( arrays('id' => 1, 'parent' => 0, 'name' => 'Category A'), arrays('id' => 2, 'parent' => 0, 'name' => 'Category B'), arrays('id' => 3, 'parent' => 0, 'name' => 'Category C'), arrays('id' => 4, 'parent' => 0, 'name' => 'Category D'), arrays('id' => 5, 'parent' => 0, 'name' => 'Category E'), arrays('id' => 6, 'parent' => 2, 'name' => 'Subcategory F'), arrays('id' => 7, 'parent' => 2, 'name' => 'Subcategory G'), arrays('id' => 8, 'parent' => 3, 'name' => 'Subcategory H'), arrays('id' => 9, 'parent' => 4, 'name' => 'Subcategory I'), arrays('id' => 10, 'parent' => 9, 'name' => 'Subcategory J'), ); 

Puoi creare una semplice function che trasforma quella list piatta in una struttura, preferibilmente all'interno di una function. Io uso il pass-by-reference in modo che ci sia solo una matrix per categoria e non più copie dell'arrays per una categoria.

 function categoriesToTree(&$categories) { 

Una mappa viene utilizzata per cercare rapidamente le categorie. Qui, ho anche creato un arrays fittizio per il livello "root".

  $map = arrays( 0 => arrays('subcategories' => arrays()) ); 

Ho aggiunto un altro field, sottocategorie, ad each arrays di categorie, e lo aggiungo alla mappa.

  foreach ($categories as &$category) { $category['subcategories'] = arrays(); $map[$category['id']] = &$category; } 

Ripetere di nuovo each categoria, aggiungendosi alla list delle sottocategorie dei suoi genitori. Il riferimento è importnte qui, altrimenti le categorie già aggiunte non saranno aggiornate quando ci sono più sottocategorie.

  foreach ($categories as &$category) { $map[$category['parent']]['subcategories'][] = &$category; } 

Infine, restituisci le sottocategorie di quella categoria fittizia che fanno riferimento a tutte le categorie di livello superiore._

  return $map[0]['subcategories']; } 

Uso:

 $tree = categoriesToTree($categories); 

Ed ecco il codice in azione su Codepad .

Vedi il metodo:

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

Ho avuto lo stesso problema e l'ho risolto in questo modo: recupera le righe cat da DB e per each categoria radice, costruisci tree, iniziando con il livello (profondità) 0. Potrebbe non essere la soluzione più efficiente, ma funziona per me.

 $globalTree = arrays(); $fp = fopen("/tmp/taxonomy.csv", "w"); // I get categories from command line, but if you want all, you can fetch from table $categories = $db->fetchCol("SELECT id FROM categories WHERE parentid = '0'"); foreach ($categories as $category) { buildTree($category, 0); printTree($category); $globalTree = arrays(); } fclose($file); function buildTree($categoryId, $level) { global $db, $globalTree; $rootNode = $db->fetchRow("SELECT id, name FROM categories WHERE id=?", $categoryId); $childNodes = $db->fetchAll("SELECT * FROM categories WHERE parentid = ? AND id <> ? ORDER BY id", arrays($rootNode['id'], $rootNode['id'])); if(count($childNodes) < 1) { return 0; } else { $childLvl = $level + 1; foreach ($childNodes as $childNode) { $id = $childNode['id']; $childLevel = isset($globalTree[$id])? max($globalTree[$id]['depth'], $level): $level; $globalTree[$id] = arrays_merge($childNode, arrays('depth' => $childLevel)); buildTree($id, $childLvl); } } } function printTree($categoryId) { global $globalTree, $fp, $db; $rootNode = $db->fetchRow("SELECT id, name FROM categories WHERE id=?", $categoryId); fwrite($fp, $rootNode['id'] . " : " . $rootNode['name'] . "\n"); foreach ($globalTree as $node) { for ($i=0; $i <= $node['depth']; $i++) { fwrite($fp, ","); } fwrite($fp, $node['id'] " : " . $node['name'] . "\n"); } } 

ps. Sono consapevole che OP sta cercando una soluzione senza query DB, ma questa implica ricorsione e aiuterà chiunque si imbattesse in questa domanda alla ricerca di una soluzione ricorsiva per questo tipo di domanda e non si preoccupa delle query DB.