• Шаблоны для вашего сайта
  • Сделать сайт
  • Реклама
  • Berry CSS
  • Albireo Framework
  • Бесплатный HTML-курс
  • Telegram-канал
  • Обратная связь
MaxSite.org
О создании сайтов и не только
Создание сайтов под ключ (Украина) →
Вход
×
или зарегистрироваться

Как из дерева сделать ul-li структуру?

PHPПросмотров: 17715Комментарии: 25 апреля 2008 г.

Получилось, что я немного меньше стал публиковать записей в блоге, но на это есть довольно веские причины. Помимо работы я постоянно занимаюсь 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 = "
<ul>";
	$open  = "</li>
<li>";
	$close = "</li>
</ul>";
	$left  = "
<ul>
<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;
}

Сорри, при переносе сайта удалились комменты, поэтому здесь, те, которые сохранились.


TedBeer :

6 апреля 2008 г. в 18:51 edit

Вот пример без level и без children(вычисляются по ходу дела) в базе. Порядок неважен, дети в массиве могут быть и до родителей, так что выборку из базы можно делать без сортировки.

$info = Array(
1 => Array
(
'category_id' => 1,
'category_id_parent' => 0,
'category_name' => 'Один'
),
2 => Array
(
'category_id' => 2,
'category_id_parent' => 1,
'category_name' => 'Два'
),
3 => Array
(
'category_id' => 3,
'category_id_parent' => 1,
'category_name' => 'Три'
),

4 => Array
(
'category_id' => 4,
'category_id_parent' => 3,
'category_name' => 'Четыре'
),
5 => Array
(
'category_id' => 5,
'category_id_parent' => 0,
'category_name' => 'Пять'
),
6 => Array
(
'category_id' => 6,
'category_id_parent' => 4,
'category_name' => 'Шесть'
)
);

$top = Array();

foreach($info as &$item){
$parentId = $item['category_id_parent'];
$id = $item['category_id'];

if( $parentId && isset($info[ $parentId])){
if( !isset($info[ $parentId]['children']))
$info[ $parentId]['children'] = Array($id);
else
$info[ $parentId]['children'][] = $id;
} else {
$top[] = $id;
}
}

function glue( $in, &$info){

$out = Array();
foreach( $in as $id){
$out[] = '<li>'. $info[$id]['category_name'];
if( isset( $info[$id]['children'])){
$out[] = '<ul>';
$out[] = glue( $info[$id]['children'], $info);
$out[] = '</ul>';
}
$out[] = '</li>';
}
return implode("
", $out);
}

echo '<ul>' . glue( $top, $info) . '</ul>';

Galchenkov :

7 апреля 2008 г. в 11:19 edit

'Один', 'level' => 0);
$categories[] = Array('name' => 'Два', 'level' => 1);
$categories[] = Array('name' => 'Три', 'level' => 1);
$categories[] = Array('name' => 'Четыре', 'level' => 2);
$categories[] = Array('name' => 'Пять', 'level' => 3);
$categories[] = Array('name' => 'Шесть', 'level' => 2);
$categories[] = Array('name' => 'Семь', 'level' => 0);

// Инициализируем переменную
$current_level = -1;

foreach ($categories as $category)
{
// Уровень отображаемого категории (узла, если хотите)
$category_level = $category['level'];

if ($current_level <li>'.$category['name'];
}

if ($current_level &gt; $category_level)
{
// Тут важно закрыть все открытые ранее списки (ведь из третьего
// уровня можно сразу попасть в первый)
echo str_repeat('</li></ul>', $current_level - $category_level + 1);
echo '<ul><li>'.$category['name'];
}

if ($current_level == $category_level)
{
echo '</li><li>' . $category['name'];
}

$current_level = $category_level;
}

// Тут тоже закрываем все открытые ранее списки
echo str_repeat('</li></ul>', $current_level + 1);

?>

Создание сайтов (Украина) →
FastCGI и CodeIgniter, мой новый сайт и секрет небольшой памяти
34 отличия блогера от колхозника или Тут есть кто живой?
twitter.com facebook.com
Другие записи сайта
Учимся работать с MF. Подвал. Дизайн
Учимся работать с MF. Подвал. Дизайн
FastCGI и CodeIgniter, мой новый сайт и секрет небольшой памяти
FastCGI и CodeIgniter, мой новый сайт и секрет небольшой памяти
Простое использование SQLite
Простое использование SQLite
Почему Яндекс идёт против вебмастеров или как вебмастеру выжить в кризис
Почему Яндекс идёт против вебмастеров или как вебмастеру выжить в кризис
Шаблон Decorator (Декоратор)
Шаблон Decorator (Декоратор)
Микро-фреймворк как основа веб-проекта на PHP
Микро-фреймворк как основа веб-проекта на PHP

Комментариев: 2 RSS

1Arreay07-07-2009 21:26

Дааа, можно всё сделать на много проще :-) Над такой задачей я бился недели две тож пишу свою ЦМС

2lorigin26-10-2009 11:54

у меня была таблица [id name owner type]

owner - ID родителя [0 - корень]

type (0 если это каталог, 1 если конечный элемент), грубо говоря папка и файл

нужно было вывести дерево.

логика примерно такая:

- берем все где владелец КОРЕНЬ.

- запоминаем idшники этих записей, а саму выборку сохраняем в results[0] - тоесть хранит все элементы первого уровня

- теперь выбираем "второй уровень", тоесть у кого owner'ы - те что нашли раньше =)

- ну и т.д.

тоесть получаем массив results где каждый элемент массива содержит выборку по определенному уровню... количество запросов сколько уровней.. редко превышает 5

function WriteTree() {
global $result; //делаем глобальной
$in_string = '0'; // параметры запроса, тоесть сначала делаем выборку "WHERE owner in (0)"
$result = array();
$level = 0; // уровень дерева
while ($in_string !='') {
$query = "SELECT * FROM catalog WHERE owner in (".$in_string.") ORDER BY id ASC";
$result[$level] = mysql_query($query) or die('ОШИБКА '.mysql_error());
$in_string = '';
$in_array = array();
$num_rows = mysql_num_rows($result[$level]);
if ($num_rows) { //если хотябы одна запись вернулась
while ($catalog_row = mysql_fetch_array($result[$level])) {
$in_array[] = $catalog_row['id']; // выбираем ID текущего уровня, чтобы патом определить узлы следуюшей вложености
}
$in_string = implode(',',$in_array);
$level++;
}

}
unset ($result[$level]); // убиваем последнюю выборку, которая пустая
//echo "уровней = ".$level;
PrintBranch(0,0);
}

а тут разворачиваем дерево, заходим в каждую ветку и выводим элементы..

function PrintBranch($level,$id) {
global $result;
$ul_write = false;
echo "\n";
mysql_data_seek($result[$level], 0);
while ($row = mysql_fetch_assoc($result[$level])) {
if ($row['owner'] == $id) {
//if (!$ul_write) echo "\n";
echo "";
echo "";
echo "".GetLangText($row['name'],'ru')."".GetLangText($row['name'],'en')."";
$new_lev = $level;
if (isset($result[$level+1]) )
PrintBranch(++$new_lev,$row['id']);
echo "\n";
}
}
echo "\n";

}
Оставьте комментарий!

Комментарий будет опубликован после проверки. Вы соглашаетесь с правилами сайта.

(обязательно)

Навигация
  • Шаблоны для MaxSite CMS 22
  • jQuery и JavaScript 6
  • Java и Android 5
  • PHP/ООП 25
  • SQL 17
  • Albireo Framework 11
  • Berry CSS 7
  • CSS, HTML, LESS, SASS 23
  • PHP 37
  • Тайм-менеджмент 9
  • Софт 37
  • SEO 13
  • Git. GitHub 3
  • CodeIgniter 5
  • Landing Page 3
  • Alpine.js 14
  • Фильмы 2
  • Дневник 55

Здесь можно заказать создание сайта (только Украина), шаблона или лендинга. Также вы можете выбрать готовые шаблоны для MaxSite CMS по небольшой цене. Также можно купить отдельные модули, компоненты для вашего сайта.

MaxSite.org
Как создать свой сайт

Услуги по созданию сайтов, блогов, лендингов
Обратная связь • Реклама на сайте
Карта сайта
Мои проекты
  • Шаблоны для вашего сайта
  • Заказать создание сайта
  • MaxSite CMS
  • Berry CSS (CSS Utilities)
  • Albireo Framework
  • UniCSS (Universal Atomic CSS)
  • Landing Page Framework
  • Бесплатные НТML-курсы
Ссылки
  • Telegram-канал
  • Github
  • Twitter
  • Telegram-бот
  • RSS
© MaxSite.org, 2006-2022. Работает на MaxSite CMS | Время: 0.2567 | SQL: 20 | Память: 4.63MB | Вход