Есть массив с пунктами меню такого вида: Код (PHP): <?php $items = array( array('id' => 1, 'title' => 'Курсы дизайнеров', 'parent' => 0), array('id' => 2, 'title' => 'Курсы ландшафтного дизайна', 'parent' => 1), array('id' => 3, 'title' => 'Курсы дизайнеров интерьера', 'parent' => 1), array('id' => 4, 'title' => 'Курсы компьютерной графики', 'parent' => 0), array('id' => 5, 'title' => 'Школа фотографии', 'parent' => 0), ); ?> Нужно сделать из этого меню, список в списке вида: Код (PHP): <ul> <li> Курсы дизайнеров <ul> <li>Курсы ландшафтного дизайна</li> <li>Курсы дизайнеров интерьера</li> </ul> </li> <li>Курсы компьютерной графики</li> <li>Школа фотографии</li> </ul> Самый простой вариант сделать так: Код (PHP): <ul class="parent"> <?php foreach ($items as $cat): ?> <?php if ($cat['parent'] == 0): ?> <li> <?php echo $cat['title']; ?> <ul class="children"> <?php foreach ($items as $subcat): ?> <?php if($subcat['parent'] == $cat['id']): ?> <li><?php echo $subcat['title']; ?></li> <?php endif; ?> <?php endforeach; ?> </ul> </li> <?php endif; ?> <?php endforeach; ?> </ul> Но тогда у пунктов без подпунктов будет пустой ul. В общем надо проверять есть ли дочерний пункт меню. Тогда я сделал так: Код (PHP): <ul class="parent"> <?php foreach ($items as $cat): ?> <?php if ($cat['parent'] == 0): ?> <li> <?php echo $cat['title']; ?> <?php $ul_open = true; $ul_close = false; ?> <?php foreach ($items as $subcat): ?> <?php if($subcat['parent'] == $cat['id']): ?> <?php if ($ul_open) : $ul_open = false; $ul_close = true; ?> <ul class="children"> <?php endif; ?> <li><?php echo $subcat['title']; ?></li> <?php endif; ?> <?php endforeach; ?> <?php if ($ul_close): ?> </ul> <?php endif; ?> </li> <?php endif; ?> <?php endforeach; ?> </ul> И это выдает списки так как надо. Но не покидает ощущение, что это тот ещё говнокод и можно сделать проще. Да к тому же я не могу быть уверен, что вложенность будет только одна и мне потом не придется допиливать ещё третий и четвертый цикл. Куда покапать? Может как-то отфильтровать массив, разбить по другому, использовать рекурсию? Почитал в мануале по функциям работы с массивами, есть и рекурсивные и с применением callback-функций. http://php.ru/manual/ref.array.html Но, скорее всего тупо из-за нехватки опыта, не увидел возможного решения.
а ты не рисуй сразу подпункты, сначала собери в массив и если он не пустой, то рисуй. Вообще можно заранее распихать за один раз ещё до начала отрисовки.
Ты хочешь чтобы мы отняли у тебя радость новых открытий? может, почему бы и нет хорошая мысль. а можно рекурсию заменить на итерацию и стек. попробуй всё. и если тебе дать готовое решение, ты и дальше не будешь видеть. так устроен мозг: если его не бодрить работой, он ленится.
Всё гениальное просто, была такая мысль, но почему-то проигнорировал. Так и сделаю. О нет, этого я не хочу. Итератор прикольная штука, надо будет поэсперементировать с ним, но пока хочу обойтись рекурсией. Запилил такую штуку, что бы дочерние пункты меню были в подмассивах: Код (PHP): function test($items, $parent = 0) { $new_items = array(); foreach($items as $id => $item) { if($parent == $item['parent']){ $new_items[$id] = $item; $new_items[$id][] = test($items, $item['id']); } } return $new_items; } $arr = test($items); echo '<pre>'.print_r($arr,1).'</pre>'; Но, тут косяк, если нет дочерних пунктов всё равно создается пустой подмассив, не могу придумать как решить эту проблему.
ок, смотри такой вариант уже формирующий вложенные <ul>. слова level и space здесь только для красоты оформления отступов, на функциональность не влияют: Код (PHP): // Print whole hierarchy makeList($items); function makeList(array $adjacencyList, $parentId = 0, $level = 0) { $brothers = array_filter( $adjacencyList, function($x) use($parentId) { return $x['parent'] == $parentId; } ); if (count($brothers)) { $space = str_repeat(' ', $level); echo "<ul>\n"; foreach ($brothers as $x) { echo "{$space} <li>{$x['title']}"; makeList($adjacencyList, $x['id'], $level + 1); echo "</li>\n"; } echo "{$space}</ul>"; } } на входе массив как в твоём первом посте
Круто, прям то что было нужно. Правда не всё понял. С замыканием (вроде это оно) ещё не знаком. Почитаю мануал, поэсперементирую, вардампом попользуюсь, разберусь. Благодарю.
Дальше надо выделить родительскую рубрику. Эмуляция перехода в подменю. $thisCat - текущая категория. Код (PHP): function makeList(array $adjacencyList, $parentId = 0, $level = 0) { $thisCat = 2; $brothers = array_filter( $adjacencyList, function ($x) use ($parentId) { return $x['parent'] == $parentId; } ); if (count($brothers)) { $space = str_repeat(' ', $level); echo "<ul>\n"; foreach ($brothers as $x) { if($thisCat == $x['id']): echo "{$space} <li><a style='background: silver' href='#'>{$x['title']}</a>"; else: echo "{$space} <li><a href='#'>{$x['title']}</a>"; endif; makeList($adjacencyList, $x['id'], $level + 1); echo "</li>\n"; } echo "{$space}</ul>"; } } Выделить текущую категорию не проблема, а вот как быть с родительской?