Построение дерева иерархии с помощью 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:
Оставить комментарий
Комментарии
1.
+10 / -3
16 сентября 2015, 17:30:35
да уж писаки.. а никто не догадался одним запросом получить данные в массив, а за тем с этим массивом и работать?
2.
+1 / -2
28 декабря 2009, 16:48:14
так как у меня структура предполагается быть очень большой порядка 300 элементов, процесс запроса к базе данных только при авторизации, дальше все из сессии =)
а запросы будут очень частыми именно старницы с деревом =( поэтому исключаем все запросы к мускулю и делаем все граматна =)
о структуре и операциях до выведения:
изначально структура была представлена матрицей смежности для ориентированного графа, если граф двудольный будет бесконечный цикл, поэтому не стоит применять, потому как необходимо еще одно условия прерывания цикла, но это мне не нада поэтому, я не делал
потом поиском в ширину от необходимого элемента до всех остальных веток получили список смежности в структуре которого массив из 3х элементов: i - текущая вершина,j - родительская вершина,mark - уровень с 0
а запросы будут очень частыми именно старницы с деревом =( поэтому исключаем все запросы к мускулю и делаем все граматна =)
о структуре и операциях до выведения:
изначально структура была представлена матрицей смежности для ориентированного графа, если граф двудольный будет бесконечный цикл, поэтому не стоит применять, потому как необходимо еще одно условия прерывания цикла, но это мне не нада поэтому, я не делал
потом поиском в ширину от необходимого элемента до всех остальных веток получили список смежности в структуре которого массив из 3х элементов: i - текущая вершина,j - родительская вершина,mark - уровень с 0
Код:
function show_tree($ParentID, $lvl = -1)
{ //// мои глобалки функций
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>';
}
{ //// мои глобалки функций
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>';
}
3.
+5 / -0
2 августа 2009, 22:22:25
Думаю не изобрету велосипед если скажу следующее:
Пример наглядный, но явно недоработанный, зато годится для начала объяснения работы с деревьями. Работать с таким деревом не удобно. Очень поможет знать есть ли у узла дети, а также текущий уровень.
| id | pid | child | level | title |
-----------------------------------
| 1 | 0 | 1 | 0 | 1. |
| 2 | 0 | 0 | 0 | 2. |
| 3 | 1 | 0 | 1 | 1.1 |
Проще сразу знать есть ли у узла дети, чем долбить рекурсиями базу.
Уровни тоже вещь очень полезная, позволяют без труда выбирать узлы одного уровня.
Пример наглядный, но явно недоработанный, зато годится для начала объяснения работы с деревьями. Работать с таким деревом не удобно. Очень поможет знать есть ли у узла дети, а также текущий уровень.
| id | pid | child | level | title |
-----------------------------------
| 1 | 0 | 1 | 0 | 1. |
| 2 | 0 | 0 | 0 | 2. |
| 3 | 1 | 0 | 1 | 1.1 |
Проще сразу знать есть ли у узла дети, чем долбить рекурсиями базу.
Уровни тоже вещь очень полезная, позволяют без труда выбирать узлы одного уровня.
4.
+1 / -2
23 января 2009, 00:05:32
Хы когда публиковал скрипт даже не смарел на коментарии ... скрипт сам придумал когда курил в очередной раз а потом сморю у y7u8 точ такойже практически только стиль чуть другой =)
5.
+9 / -1
17 января 2009, 03:31:22
Кодеру руки оторвать ...
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);
?>
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);
?>
6.
+14 / -1
22 декабря 2008, 02:44:06
афтар. убейся об стену. это самый ужасный код для построения дерева. который тока можно придумать.
тока в твоем примере 16!!! запросов в базу
а если категорий будет больше раза в в 3-4???
тока в твоем примере 16!!! запросов в базу
а если категорий будет больше раза в в 3-4???
7.
+9 / -3
29 июня 2007, 11:34:33
Так и не понял зачем там уровень? Куда проще так
Код:
function ShowTree ($parent_id) {
$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);
$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);
8.
+4 / -3
29 июня 2007, 11:33:28
9.
+3 / -3
10 декабря 2005, 15:57:40
А смысл в том, что без global $lvl можно было б не выполнять оператор $lvl--; а куда ж без него:)
а оптимизировать число запросов имхо не возможно, здесь же нет какой либо зависимости - дерево, оно и есть дерево
а оптимизировать число запросов имхо не возможно, здесь же нет какой либо зависимости - дерево, оно и есть дерево
10.
+0 / -4
11 ноября 2005, 15:24:55
<blockquote><small>Цитата:<hr size=1>
function ShowTree($ParentID, $lvl) {
...
global $lvl;
[/quote]
если global - какой смысл в параметре $lvl ?
Да и в принципе - сколько будет запросов к таблице с приличным числом записей
function ShowTree($ParentID, $lvl) {
...
global $lvl;
[/quote]
если global - какой смысл в параметре $lvl ?
Да и в принципе - сколько будет запросов к таблице с приличным числом записей