PHP: Пример самого простого кода — дерево категорий сайта

Одна из типичных задач — рекурсивное дерево, например, категории сайта… Как сделать, и как сделать правильно)

Начальный кусочек — простейшее соединение с базой, предположим там уже есть таблица menu с полями id, title, id_parent (где собственно родитель — это номер родительской категории / меню / чего угодно, если он 0 — это корневая, начальная категория)

<?php
 
define('DB_HOST',           'localhost');
define('DB_NAME',           'ваша база');
define('DB_USER',           'ваш логин');
define('DB_PASS',           'ваш пароль');
 
$db_link = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);

Против лома нет приема без рекурсии

Самый простейший способ сделать дерево — взять родителей список, и потом деток у каждого… заметьте — чтобы тут добавить третий уровень — Вы еще глубже по коду уйдете, поэтому мы введем рекурсивную функцию на следующем шаге

<?php
 
$res = mysqli_query($db_link, 'SELECT * FROM menu WHERE id_parent=0');
$rows = array();
while($row = mysqli_fetch_assoc($res)) {
	$rows[] = $row;
}
echo '<ul>';
foreach($rows AS $row) {
	echo '<li>' . $row['title'];
	$res = mysqli_query($db_link, 'SELECT * FROM menu WHERE id_parent=' . $row['id']);
	if ($res) {
		echo '<ul>';
		while ($row2 = mysqli_fetch_assoc($res)) {
			echo '<li>' . $row2['title'];
		}
		echo '</ul>';
	}
	echo '</li>';
}
echo '</ul>';

Против лома нет приема с рекурсией

Уже чуть лучше — только ведь мы можем с нулевого уровня и стартовать (вывод у нас сейчас не отличается)

<?php
$res = mysqli_query($db_link, 'SELECT * FROM menu WHERE id_parent=0');
$rows = array();
while($row = mysqli_fetch_assoc($res)) {
	$rows[] = $row;
}
echo '<ul style="display:none">';
foreach($rows AS $row) {
	echo '<li>' . $row['title'];
	get_childs($row['id'], $db_link);
	echo '</li>';
}
echo '</ul>';
 
function get_childs($parent, $db_link) {
	$res = mysqli_query($db_link, 'SELECT * FROM menu WHERE id_parent=' . $parent);
	if ($res) {
		echo '<ul>';
		while ($row = mysqli_fetch_assoc($res)) {
			echo '<li>' . $row['title'];
			get_childs($row['id'], $db_link);
			echo '</li>';
		}
		echo '</ul>';
	}
}

Против лома нет приема с рекурсией с нулевого уровня

Еще чуть лучше, но посмотрите сколько у нас запросов … а выводим мы всего лишь всю таблицу) т.е. это плохой способ нагрузить базу кучей мелких, когда можно один большой

<?php
$html = '';
get_childs(0, $db_link, $html);
echo $html;
 
function get_childs($parent, $db_link, &$html, $level = 0)
{
	$res = mysqli_query($db_link, 'SELECT * FROM menu WHERE id_parent=' . $parent);
	$html .= $parent . ' : ' . $level . ' :  select!<br/>';
	if ($res) {
		echo '<ul>';
		while ($row = mysqli_fetch_assoc($res)) {
			echo '<li>' . $row['title'];
			get_childs($row['id'], $db_link, $html, $level + 1);
			echo '</li>';
		}
		echo '</ul>';
	}
}

Лучше без лома

Упорядочим заранее строки для вывода

$res = mysqli_query($db_link, 'SELECT * FROM menu');
$rows = array();
while ($row = mysqli_fetch_assoc($res)) {
	$rows[$row['id_parent']][] = $row;
}
get_childs(0, $rows);
echo '<br/>' . (microtime(true) - $start);
 
function get_childs($parent, &$rows)
{
	if (!isset($rows[$parent])) return false;
	echo '<ul>';
	foreach($rows[$parent] AS $row) {
		echo '<li>' . $row['title'];
		get_childs2($row['id'], $rows);
		echo '</li>';
	}
	echo '</ul>';
 
}

Тестирование

Чтобы совсем красиво проверить — можете алгоритм запускать на сгенерированных заранее данных — так интереснее и явно видна разница скорости (почему один запрос лучше кучи)

$parent_id = 0;
for($i = 100; $i<1000; $i++) {	
	$res = mysqli_query($db_link, 'INSERT INTO menu (`id`, `id_parent`, `title`) 
					VALUES ("' . $i . '", "' . $parent_id . '", "Подменю ' . $i . '")');
        $parent_id = rand(100, $i);
}

Можно дополнительно прочесть про технологию выделения рекурсивных шагов — Первые шаги: Готовое решение рекурсии категорий

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

XHTML: Вы можете использовать такие теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre lang="" line="" escaped="" cssfile="">