Вывод древовидной структуры (PHP+MySQL)

AX
На сайте с 20.09.2008
Offline
133
10790

Уважаемые, форумчане, помогите разобраться со следующей проблемой:

Есть БД в ней таблица:

id       pid       name

1 0 main
13 0 title-1
14 0 title-2
15 13 title-1-1
16 13 title-1-2
17 14 title-2-1
18 14 title-2-2
19 0 contacts

Соответственно, на выходе хочу получить дерево разделов с подразделами (т.е делаю скрипт карты сайта), вида:

main

title-1
title-1-1
title-1-2
title-2
title-2-1
title-2-2
contacts

Пробую запросами к БД, но там выводится все по-порядку и нет разграничений =( Помогите разбораться с проблемкой, заранее спасибо. :idea:

CE
На сайте с 30.01.2008
Offline
73
#1

Сам что-то пробовал? Рекурсивную функцию напиши, которая будет посылать запрос к базе, получать список записей данного уровня вложенности, потом пробегать по этому списку и запускать себя снова с новыми параметрами.

vlasoff
На сайте с 31.08.2007
Offline
101
#2

я вот так генерю карту:


function gen_map_inside($catId,$inLevel) {
global $SITE_MAP;
$inLevel++;
$q = mysql_query('SELECT `strid`,`parent_id`,`name` FROM `structure` WHERE `type` = 0 AND `active` = 1 AND `parent_id` = '.$catId.' ORDER BY `menu_weight` DESC, `name` ASC;');
$a = 0;
while ($r = mysql_fetch_array($q)) {
$strid = $r['strid'];
if (check_permission($strid)) {
if (!$a) {
$SITE_MAP.= '<ul>';
$a = 1;
}
$SITE_MAP.= '<li><a href="'.get_url_by_id($strid).'">'.stripslashes($r['name']).'</a></li>'."\n";
if ($inLevel <= MAP_INLAY_COUNT) gen_map_inside($strid,$inLevel);
}
}
if ($a) $SITE_MAP.= '</ul>';
}

$SITE_MAP.= '<div id="sitemap">';
$inLevel = $catId = 0;
$q = mysql_query('SELECT `strid`,`parent_id`,`name`,`mod` FROM `structure` WHERE `type` = 0 AND `active` = 1 AND `parent_id` = '.$catId.' ORDER BY `menu_weight` DESC, `name` ASC;');
$a = 0;
while ($r = mysql_fetch_array($q)) {
$strid = $r['strid'];
if (check_permission($strid)) {
if (!$a) {
$SITE_MAP.= '<ul>';
$a = 1;
}
$SITE_MAP.= '<li><a href="'.get_url_by_id($strid).'">'.stripslashes($r['name']).'</a></li>'."\n";
if ($inLevel <= MAP_INLAY_COUNT && $r['mod'] != 'lists') gen_map_inside($strid,$inLevel);
}
}
if ($a) $SITE_MAP.= '</ul>';
$SITE_MAP.= '</div>';
AM
На сайте с 12.09.2007
Offline
47
#3

если особо не париться со скоростью и оптимизацией, то примерно так:



$tree = $this->get_tree(0);
echo $this->PrintTree($tpl, $tree);

function get_tree($id_parent = 0)
{

$categories = $this->db->fetchall_array('SELECT * FROM '.TAB_PREFIX.'subrazdels WHERE id_parent = '.$id_parent);

foreach ($categories as $k => $cat)
$categories[$k]['subrazdels'] = $this->get_tree($cat['id_subrazdel']);

return $categories;
}

function PrintTree(&$tpl, $categories, $level=0)
{
foreach ($categories as $cat) {
$tpl->set_var('/tree/MARGIN', 10 + 15 * $level);
$tpl->set_var('/tree/CATEGORY_NAME', $cat['name']);
$tpl->set_var('/tree/DESCRIPTION', $cat['description']);
$tpl->set_var('/tree/CAT_LINK', href('id_razdel='.$cat['id_subrazdel']));
$tpl->parse('/tree/', 1);
if ($cat['subrazdels'])
$this->PrintTree($tpl, $cat['subrazdels'], $level+1);
}
}

С уважением, Морозов Андрей, разработчик проекта eTXT.ru (http://www.etxt.ru/?r=morozov), icq 55377667
DI
На сайте с 03.01.2007
Offline
123
#4

зачем такие простые вещи делать рекурсивно? Нужно экономить ресурсы, если только на этот блок потратится 10-30 запросов, а таких блоков на странице 10-20? Вот так и появляется новая статья расходов "оперативная память сервера", вместо оптимизации запроса.

Обычно, если вложенность неограниченна, проще вытащить нужные данные одним запросом, разложить их по массивам, и с ними уже работать. В одном массиве - информация о parent'e вида $Array[$pid][$id], получая id разделов - из другого массива получаем остальные данные по $id и рисуем карту ($Pages[$id]['name'], $Pages[$id]['url'] и т.п.).

Если карта строго двухуровневая (по примерам, которые приводятся авторами в подобных темах, нихрена не угадать, что же он конкретно имел в виду), а с массивами возиться не хочется - то можно просто вытащить информацию о втором уровне (WHERE pid<>0 ORDER BY pid,id), а название родителя подцепить в тот же запрос через LEFT JOIN, и вывести так, как я предлагал в соседнем топике.

Высказывание идиотского утверждения требует на порядок меньше усилий, чем его последовательное и обоснованное опровержение и более того, иногда это опровержение вообще невозможно. © (http://zhurnal.lib.ru/s/shapiro_m_a/raspidiota.shtml)
AM
На сайте с 12.09.2007
Offline
47
#5

все уже обсуждалось много раз /ru/forum/272462

AX
На сайте с 20.09.2008
Offline
133
#6

Скажу так, моего опыта маловато, чтобы понять =)

Подумал про идею, если считать запрос к БД в массив, слышал, что есть массивы с ключами.

А потом из массива вытаскивать нужное, т.е если есть подразделы, то сначала вывести элемент массива с разделом-родителем, а затем найти элементы подразделов. И все это вывести, и так до тех пор пока не дойду до последнего элемента.

Только как это реализовать?

AM
На сайте с 12.09.2007
Offline
47
#7

Изучайте код, $list - это полный список категорий вида [id_category] => array('id_parent' => .., 'name' => ..) ... на оптимальность не претендует, но работает шустренько


// функция построения списка всех подкатегорий для текущей категории
function getCategorySub($list, $id_parent = 0)
{
$my_category = array();

// построение внутреннего дерева категорий для последующей обработки
foreach ($list as $key => $val)
if ($val['id_parent'])
$my_category[$val['id_parent']]['childs'][] = $key;

// рекурсивный выбор потомков из внутреннего дерева категорий
if (isset($my_category[$id_parent])) {
foreach ($my_category[$id_parent]['childs'] as $v)
$tmp[] = $v;
$my_category = recursSubCategory($my_category, $my_category[$id_parent]['childs']);
$my_category = array_merge($my_category, $tmp);
}
else $my_category = array();

return $my_category;
}

// рекурсивная функция сортировки дерева категорий
function recursSubCategory($common_list, $local_list)
{
$cats = array();

foreach ($local_list as $key => $val) {
if (isset($common_list[$val]) && is_array($common_list[$val]['childs'])) {
foreach ($common_list[$val]['childs'] as $v)
$cats[] = $v;
$tmp = recursSubCategory($common_list, $common_list[$val]['childs']);
if ($tmp) $cats = array_merge($cats, $tmp);
}
}

return $cats;
}

tommy-gung
На сайте с 22.11.2006
Offline
300
#8

если записей немного, согласен с DenIT, что проще, думаю и быстрее, в массив выбрать все и разобрать


function sort_cat($cats, $pid, $lvl) {
foreach($cats as $cat) {
if($cat['pid'] == $pid) {
$s = "";
for($k = 0; $k < $lvl; $k++) $s .= " - ";
echo $s.$cat['name']."<br />\n";
sort_cats($cats, $cat['id'], $lvl + 1);
}
}
}
Здесь не могла быть ваша реклама
S
На сайте с 15.07.2008
Offline
30
#9

Если не хочешь переделывать структуру БД, то поможет stored procedure.

Если есть желание подолбаться и сделать по-человечески, то придётся разбираться с Nested Sets.

Почитать можно здесь, например: http://phpclub.ru/detail/article/db_tree

Способы с пачкой запросов или полной вычиткой дерева с дальнейшим разбором - это кошерно только на очень маленьких деревьях. Если это меню, то прокатит. Если что-то большее, придётся поработать головой.

Банки Украины (http://www.bankstore.com.ua) Генератор сайтмепов (/ru/forum/272468) Ода Гугльботу (/ru/forum/285758)

Авторизуйтесь или зарегистрируйтесь, чтобы оставить комментарий