Как рекурсивно построить <select> с неизвестной глубиной дерева

У меня есть таблица MySQL с древовидной структурой данных. Поля: _id , name и parentId . Если в записи нет родителя, parentId умолчанию parentId 0. Таким образом, я могу построить массив, а затем рекурсивно распечатать каждую запись.

Встроенный массив выглядит следующим образом:

 Array ( [1] => Array ( [parentId] => 0 [name] => Countries [_id] => 1 [children] => Array ( [2] => Array ( [parentId] => 1 [name] => America [_id] => 2 [children] => Array ( [3] => Array ( [parentId] => 2 [name] => Canada [_id] => 3 [children] => Array ( [4] => Array ( [parentId] => 3 [name] => Ottawa [_id] => 4 ) ) ) ) ) [5] => Array ( [parentId] => 1 [name] => Asia [_id] => 5 ) [6] => Array ( [parentId] => 1 [name] => Europe [_id] => 6 [children] => Array ( [7] => Array ( [parentId] => 6 [name] => Italy [_id] => 7 ) [11] => Array ( [parentId] => 6 [name] => Germany [_id] => 11 ) [12] => Array ( [parentId] => 6 [name] => France [_id] => 12 ) ) ) [8] => Array ( [parentId] => 1 [name] => Oceania [_id] => 8 ) ) ) ) 

Печать неупорядоченного списка <ul> очень проста с рекурсией. Вот функция, которую я использую:

 function toUL ($arr) { $html = '<ul>' . PHP_EOL; foreach ( $arr as $v ) { $html.= '<li>' . $v['name'] . '</li>' . PHP_EOL; if ( array_key_exists('children', $v) ) { $html.= toUL($v['children']); } } $html.= '</ul>' . PHP_EOL; return $html; } 

Но я застрял в печати <select> по древовидной структуре:

 Countries -- America ---- Canada ------ Ottawa -- Asia -- Europe ---- Italy ---- Germany ---- France -- Oceania 

Я думал, что печатать -- столько раз, сколько глубина элемента, но я не знаю, как рассчитать глубину.

Мой вопрос: возможно ли построить <select> не зная глубины?

Заранее спасибо.

Передайте параметр для подсчета итерации как $pass

 function toUL ($arr, $pass = 0) { $html = '<ul>' . PHP_EOL; foreach ( $arr as $v ) { $html.= '<li>'; $html .= str_repeat("--", $pass); // use the $pass value to create the -- $html .= $v['name'] . '</li>' . PHP_EOL; if ( array_key_exists('children', $v) ) { $html.= toUL($v['children'], $pass+1); } } $html.= '</ul>' . PHP_EOL; return $html; } 

Ваша проблема уже решена в SPL. Документы RecursiveIteratorIterator содержат информацию о глубине элемента:

 $it = new RecursiveIteratorIterator(new RecursiveArrayIterator($array), SELF_FIRST); foreach ($it as $key => $element) { if ($key !== 'name') continue; $inset = str_repeat('--', $it->getDepth()); printf('<option>%s %s</option>', $inset, $element); } 
 function toSelect($arr, $depth = 0) { $html = ''; foreach ( $arr as $v ) { $html.= '<option>' . str_repeat("--", $depth) . $v['name'] . '</option>' . PHP_EOL; if ( array_key_exists('children', $v) ) { $html.= toSelect($v['children'], $depth++); } } return $html; } 

Мое окончательное решение (спасибо Starx и varan):

 function toSelect ($arr, $depth=0) { $html = ''; foreach ( $arr as $v ) { $html.= '<option value="' . $v['_id'] . '">'; $html.= str_repeat('--', $depth); $html.= $v['name'] . '</option>' . PHP_EOL; if ( array_key_exists('children', $v) ) { $html.= toSelect($v['children'], $depth+1); } } return $html; } echo '<select>'; echo toSelect($array); echo '</select>'; 

Даже решение RecursiveIteratorIterator является хорошим (спасибо hakre).

Привет, ребята, вы могли бы сделать что-то вроде этого: у меня есть этот объект для $tree :

  Array ( [0] => stdClass Object ( [id] => 1 [nombre] => Category 1 [subcategorias] => Array ( [0] => stdClass Object ( [id] => 4 [nombre] => Category 1.1 ) ) ) [1] => stdClass Object ( [id] => 2 [nombre] => Category 2 ) [2] => stdClass Object ( [id] => 3 [nombre] => Category 3 [subcategorias] => Array ( [0] => stdClass Object ( [id] => 5 [nombre] => Category 3.1 [subcategorias] => Array ( [0] => stdClass Object ( [id] => 6 [nombre] => Category 3.1.1 ) ) ) ) ) ) 

Затем Вот как создать массив для выбора HTML:

  $tree=array();// PUT HERE YOUR TREE ARRAY $arrayiter = new RecursiveArrayIterator($tree); $iteriter = new RecursiveIteratorIterator($arrayiter); $lista=array(); $i=0; foreach ($iteriter as $key => $value) { $id=$iteriter->current(); $iteriter->next(); $nivel=$iteriter->getDepth(); $nombre=str_repeat('-',$nivel-1).$iteriter->current(); $lista[$id]=$nombre; } 

Вы получите что-то вроде этого:

  Array ( [1] => Category 1 [4] => --Category 1.1 [2] => Category 2 [3] => Category 3 [5] => --Category 3.1 [6] => ----Category 3.1.1 ) 

Затем вам просто нужно создать параметры для выбора с помощью простого foreach.

Вы можете передать длину функции и прирастить ее при вызове рекурсивно, а затем использовать переменную $length для определения глубины

 function toUL ($arr, $length = 0) { $html = '<ul>' . PHP_EOL; foreach ( $arr as $v ) { $html.= '<li>' . $v['name'] . '</li>' . PHP_EOL; if ( array_key_exists('children', $v) ) { $html.= toUL($v['children'], $length++); } } $html.= '</ul>' . PHP_EOL; return $html; } 

Это то, что я обычно делаю для отслеживания глубины рекурсии, и обычно это делается

 function toUL ($arr, $depth = 0) { // ... $html .= toUL($v['children'], $depth+1); 

вы можете использовать optgroup как итератор. Например:

 <select name="list"> <option value=1>1st option</option> <optgroup> <option value=10>1st on 1st group</option> </optgroup> </select> 

если вы хотите php, вы можете попробовать следующее:

 <?PHP function toSELECT($arr,$depth=0) { $html=""; if(is_array($arr)) { $html.=($depth==0)?"<select name='html'>\n":""; foreach ($arr as $key=>$value) { if(is_array($arr[$key])) { $html.=str_repeat("\t",$depth)."<optgroup>\n"; $html.=str_repeat("\t",$depth).toHTML($arr[$key],$depth+1); $html.=str_repeat("\t",$depth)."</optgroup>\n"; } else { $html.=str_repeat("\t",$depth)."<option value='".$value."'>".$key."</option>\n"; } } $html.=($depth==0)?"</select>\n":""; } return $html; } ?>