Как из дерева сделать ul-li структуру?
Получилось, что я немного меньше стал публиковать записей в блоге, но на это есть довольно веские причины. Помимо работы я постоянно занимаюсь MaxSite CMS. Вот есть доказательства.
Столкнулся с одной проблемой, может кто поможет её решить. (Самому уже не хвататет уже ни терпения, ни ума). ![]()
Задача связана с выводом рубрик. Есть таблица рубрик, где есть поля:
- category_id (номер рубрики)
- category_id_parent (номер родителя)
Теоретически этого хватает, чтобы организовать древовидную структуру (массив).
Я её реализовал классическим способом: через рекурсию. То есть для поиска «детей» выполняется еще один SQL-запрос. Соответственно при большом количестве рубрик и её «ветвистости» получается довольно много SQL-запросов.
Частично эту проблему можно решить за счет кэширования: получается где-то раза в два меньше.
Погуглив я нашел еще один способ создания деревьев: в нем указывается отступ слева (level). Например так («Код 1»):
001 level = 0 002 level = 1 003 level = 1 004 level = 2 005 level = 1
Путем различных манипуляций с массивом я смог сделать аналогичную структуру рубрик. При этом понадобился всего один SQL-запрос. Получилась примерно так:
[1] => Array
(
[category_id] => 1
[category_id_parent] => 0
[category_name] => Новости
[category_menu_order] => 4
[parents] => 0
[childs] =>
[level] => 0
)
[3] => Array
(
[category_id] => 3
[category_id_parent] => 0
[category_name] => CodeIgniter
[category_menu_order] => 1
[parents] => 0
[childs] => 6 5
[level] => 0
)
[6] => Array
(
[category_id] => 6
[category_id_parent] => 3
[category_name] => Еще
[category_menu_order] => 0
[parents] => 3
[childs] =>
[level] => 1
)
...
Это «одномерная» структура, где сразу указываются и level (отступ), и все «дети» (childs), и родитель (parents). Всей этой информации по идее должно хватить на то, чтобы выстраивать и получать любые рубрики.
Теперь, чтобы выстоить рубрики по уровню, вполне достаточно одного level. И в принципе сделать это с помощью str_pad - две строчки кода (результат будет выглядеть как «код 1»).
Но дальше - ступор... Потратил несколько дней, но так и не смог придумать, каким образом все это конвертировать в ul-li. Сложность в том, что простыми заменами кажется не обойтись из-за вложенности тэгов. Чтобы было совсем понятно, нужно конвертировать «Код 1» (или сам массив) вот в это:
- 001
- 002
- 003
- 004
- 005
HTML-код:
<ul> <li>001 <ul> <li>002</li> <li>003 <ul> <li>004</li> </ul> </li> <li>005</li> </ul> </li> </ul>
Кто-то знает как решить эту задачку?
[upd] Вот мой вариант решения, основанный только на level. Передполагается, что массив ($r) уже отсортирован в нужной последовательности.
function mso_tree_print($r)
{
$out = "\n<ul>";
$open = "</li>\n<li>";
$close = "</li>\n</ul>";
$left = "\n<ul>\n<li>";
$old_level = 0;
$open_li = 0;
foreach ($r as $key=>$val)
{
$level = $val['level'];
if ($level == $old_level)
{
$out .= $open . $val['category_name'];
}
elseif ($level > $old_level)
{
$open_li = $open_li + $level - $old_level;
$out .= $left . $val['category_name'];
}
else // $level < $old_level
{
if (( $open_li) > 0)
$out .= str_repeat($close, $open_li);
$out .= $open . $val['category_name'];
$open_li = 0;
}
$old_level = $level;
}
$out .= $close;
$out = str_replace("<ul></li>", "<ul>", $out);
return $out;
}
Постоянная ссылка: http://maxsite.org/?p=372
Версия для печати
