Построение дерева иерархии с помощью PHP / MySQL
Рассмотрим пример построения дерева иерархии (в развернутом виде) на основе информации из базы данных с помощью PHP и MySQL. Ключ к решению данной задачи - использование рекурсивной функции. Иерархия разделов будет храниться в таблице базы данных MySQL.
Ниже на скриншоте показана данная таблица (catalogue):

- id - первичный ключ таблицы
- pid - id родительского раздела
Далее напишем следующий PHP-скрипт:
1. Файл dbopen.php (открывает соединение с MySQL)
<?php
$hostName = "";
$userName = "yura";
$password = "yura";
$databaseName = "tree";
if (!($link=mysql_connect($hostName,$userName,$password))) {
printf("Ошибка при соединении с MySQL !\n");
exit();
}
if (!mysql_select_db($databaseName, $link)) {
printf("Ошибка базы данных !");
exit();
}
?>
2. Файл index.php (основной скрипт)
<?php
include( "dbopen.php" );
function ShowTree($ParentID, $lvl) {
global $link;
global $lvl;
$lvl++;
$sSQL="SELECT id,title,pid FROM catalogue WHERE pid=".$ParentID." ORDER BY title";
$result=mysql_query($sSQL, $link);
if (mysql_num_rows($result) > 0) {
echo("<UL>\n");
while ( $row = mysql_fetch_array($result) ) {
$ID1 = $row["id"];
echo("<LI>\n");
echo("<A HREF=\""."?ID=".$ID1."\">".$row["title"]."</A>"." \n");
ShowTree($ID1, $lvl);
$lvl--;
}
echo("</UL>\n");
}
}
ShowTree(0, 0);
mysql_close($link);
?>
Всю работу выполняет рекурсивная функция ShowTree(). Ниже на скриншоте показан пример работы index.php:

Оставить комментарий
Оставлять комментарии могут только зарегистрированные пользователи.
Если вы не являетесь зарегистрированным пользователем, то вам необходимо зарегистрироваться. Регистрация бесплатна. Если вы уже зарегистрированы на CodeNet, то вам необходимо ввести логин и пароль в верхней (Alt-U) части страницы.
Комментарии
1. kyle / 28 декабря 2009, 16:48:14

так как у меня структура предполагается быть очень большой порядка 300 элементов, процесс запроса к базе данных только при авторизации, дальше все из сессии =)

а запросы будут очень частыми именно старницы с деревом =( поэтому исключаем все запросы к мускулю и делаем все граматна =)
о структуре и операциях до выведения:
изначально структура была представлена матрицей смежности для ориентированного графа, если граф двудольный будет бесконечный цикл, поэтому не стоит применять, потому как необходимо еще одно условия прерывания цикла, но это мне не нада поэтому, я не делал
потом поиском в ширину от необходимого элемента до всех остальных веток получили список смежности в структуре которого массив из 3х элементов: i - текущая вершина,j - родительская вершина,mark - уровень с 0
{ //// мои глобалки функций
global $info, $DB;
$lvl++;
// text глабальная переменная содержимого на вывод
$info->text .= '<UL>';
//главный цикл \/количество элементов в стеке (можно воскользоваться стандартной функцией count(array) )
for( $i = 1; $i < $info->hierar_queue[count]; $i++ )
{
// если уровень в списке совпадает с анализируемым в функции и родительский элемент соотвествует текущему
if($info->hierar_queue[$i][mark] == $lvl and $ParentID == $info->hierar_queue[$i][j])
{
//это понятно
$ID1 = $info->hierar_queue[$i];
// тоже
$info->text .= '<LI>';
// тоже
$info->text .= '<A HREF="index.php?act=main&dep='.$ID1.'">'.$DB->query_dep($ID1).'</A><br>';
// рукурсируем функцию с родительским - текущий и тот же уровень
// в начале функции он поднимется =)
$this->show_tree($info->hierar_queue[$i],$lvl);
// долго я ее дебагил чтобы эту строку чтобы написать
// возвращаем предыдущий элемент по выходу из функции
$ParentID = $info->hierar_queue[$i][j];
}
}
//уменьшаем уровень на выходе из функции
$lvl--;
$info->text .= '</UL>';
}
2. BugiVugi / 02 августа 2009, 22:22:25+1 / -0

Думаю не изобрету велосипед если скажу следующее:

Пример наглядный, но явно недоработанный, зато годится для начала объяснения работы с деревьями. Работать с таким деревом не удобно. Очень поможет знать есть ли у узла дети, а также текущий уровень.
| id | pid | child | level | title |
-----------------------------------
| 1 | 0 | 1 | 0 | 1. |
| 2 | 0 | 0 | 0 | 2. |
| 3 | 1 | 0 | 1 | 1.1 |
Проще сразу знать есть ли у узла дети, чем долбить рекурсиями базу.
Уровни тоже вещь очень полезная, позволяют без труда выбирать узлы одного уровня.
3. 747Nook / 23 января 2009, 00:05:32+0 / -1

Хы когда публиковал скрипт даже не смарел на коментарии ... скрипт сам придумал когда курил в очередной раз а потом сморю у y7u8 точ такойже практически только стиль чуть другой =)


4. 747Nook / 17 января 2009, 03:31:22+2 / -0

Кодеру руки оторвать ...

connect.php
<?
$db_host = 'localhost';
$db_user = 'root';
$db_pasw = '';
$db_name = 'zendown';
$db_info = mysql_connect($db_host, $db_user, $db_pasw);
mysql_query("SET NAMES `utf8`");
if(!mysql_select_db($db_name, $db_info))
{
print 'Системная ошибка. Повторите попытку через несколько минут!<br>';
echo mysql_error();
echo '<a href="java script:history.back()">Назад</a>';
die();
}
?>
index.php
<?
include "config.php";
function drevoupp($parentId){
$query = mysql_query("SELECT * from drevo");
while($row=mysql_fetch_array($query)){
$title = $row['title'];
$pid = $row['pid'];
$id = $row['id'];
$op = $row['op'];
$idd = $parentId;
if($idd == $pid){
echo "<li><a href=\"#\">$title</a>";
echo "<ul>";
drevoupp($id);
echo "</ul>";
echo "</li>";
}
}
}
drevoupp(0);
?>
5. dodther / 22 декабря 2008, 02:44:06+7 / -0

афтар. убейся об стену. это самый ужасный код для построения дерева. который тока можно придумать.

тока в твоем примере 16!!! запросов в базу
а если категорий будет больше раза в в 3-4???
6. y7u8 / 29 июня 2007, 11:34:33+1 / -1

Так и не понял зачем там уровень? Куда проще так 

$sql = "SELECT `tree_id`, `parent_id`, `title` FROM `tree` WHERE `parent_id` = $parent_id ORDER BY `title`";
$result = mysql_query($sql);
if (mysql_num_rows($result) > 0) {
echo '<ul>';
while ($row = mysql_fetch_array($result)) {
echo '<li><a href="/?tree_id='.$row['tree_id'].'">'.$row['title'].'</a></li>';
ShowTree ($row['tree_id']);
}
echo '</ul>';
}
}
ShowTree (0);
7. y7u8 / 29 июня 2007, 11:33:28+1 / -0



8. Rusty Cat / 10 декабря 2005, 15:57:40+0 / -1

А смысл в том, что без global $lvl можно было б не выполнять оператор $lvl--; а куда ж без него:)

а оптимизировать число запросов имхо не возможно, здесь же нет какой либо зависимости - дерево, оно и есть дерево
9. pribor / 11 ноября 2005, 15:24:55

<blockquote><small>Цитата:<hr size=1> 

function ShowTree($ParentID, $lvl) {
...
global $lvl;
[/quote]
если global - какой смысл в параметре $lvl ?
Да и в принципе - сколько будет запросов к таблице с приличным числом записей
