Многоуровневый select из базы данных

В скриптах используется MySQL-таблица `category` с полями `id`, `parent`, `name`, где поле `parent` содержит id родителя.

Оформление вложенности пробелами

Обычный пробел в начале текста элемента option не выводится, поэтому нужно использовать мнемонику   – широкий пробел. В начале получаем все записи из БД в виде ассоциативного массива. С помощью функции array_to_tree() преобразуем его в древовидный, к элементам массива добавляется элемент «children» в который перемещаются все дочерние элементы. С помощью функции out_options() рекурсивно выводятся все элементы массива. Во втором аргументе функции out_options() указывается id элемента, которому нужно установить selected.

<?php
$dbh = new PDO('mysql:dbname=таблица;host=localhost', 'логин', 'пароль');
$sth = $dbh->prepare("SELECT * FROM `category` ORDER BY `name`");
$sth->execute();
$category = $sth->fetchAll(PDO::FETCH_ASSOC);
$category = array_to_tree($category);
 
function array_to_tree($array, $sub = 0)
{
	$a = array();
	foreach($array as $v) {
		if($sub == $v['parent']) {
			$b = array_to_tree($array, $v['id']);
			if(!empty($b)) {
				$a[$v['id']] = $v;
				$a[$v['id']]['children'] = $b;
			} else {
				$a[$v['id']] = $v;
			}
		}
	}
	return $a;
}
 
function out_options($array, $selected_id = 0, $level = 0) 
{
	$level++;
	$out = '';
	foreach ($array as $i => $row) {
		$out .= '<option value="' . $row['id'] . '"';
		if ($row['id'] == $selected_id) {
			$out .= ' selected';
		}
		$out .= '>';
		
		if ($level > 1) {
			$out .= str_repeat('#emsp;', $level - 1);
		}
 
		$out .= $row['name'] . '</option>';
 
		if (!empty($row['children'])) {
			$out .= out_options($row['children'], $selected_id, $level);
		}
	}
	return $out;
}
?>


<select name="category">
	<option></option>
	<?php echo out_options($category, 0); ?>
</select>

Результат:

Оформление символами псевдографики

Оформление ветвей дерева с помощью символов ├ и └:

<?php
$dbh = new PDO('mysql:dbname=таблица;host=localhost', 'логин', 'пароль');
$sth = $dbh->prepare("SELECT * FROM `category` ORDER BY `name`");
$sth->execute();
$category = $sth->fetchAll(PDO::FETCH_ASSOC);
$category = array_to_tree($category);
 
function array_to_tree($array, $sub = 0)
{
	$a = array();
	foreach($array as $v) {
		if($sub == $v['parent']) {
			$b = array_to_tree($array, $v['id']);
			if(!empty($b)) {
				$a[$v['id']] = $v;
				$a[$v['id']]['children'] = $b;
			} else {
				$a[$v['id']] = $v;
			}
		}
	}
	return $a;
}
 
function out_options($array, $selected_id = 0, $level = 0) 
{
	$level++;
	$out = '';
	foreach ($array as $i => $row) {
		$out .= '<option value="' . $row['id'] . '"';
		if ($row['id'] == $selected_id) {
			$out .= ' selected';
		}
		$out .= '>';
 
		if ($level > 1) {
			if ($level > 2) {
				$out .= str_repeat('&nbsp;', $level - 1);
			}
			
			$keys = array_keys($array);
			$last_keys = $keys[count($array) - 1];			
			if ($last_keys != $i) {
				$out .= '├';
			} else {
				$out .=  '└';
			}
		}
 
		$out .= $row['name'] . '</option>';
 
		if (!empty($row['children'])) {
			$out .= out_options($row['children'], $selected_id, $level);
		}
	}
	return $out;
}
?>
 
<select name="category">
	<option></option>
	<?php echo out_options($category, 0); ?>
</select>

Результат:

Использование optgroup

Использование optgroup label="" оправдано если необходимо выбрать только крайнюю категорию в дереве, но optgroup не поддерживает вложенность и никакие пробельные символы в начале label="...". Поэтому в примере используется &mdash; – широкое тире.

<?php
$dbh = new PDO('mysql:dbname=таблица;host=localhost', 'логин', 'пароль');
$sth = $dbh->prepare("SELECT * FROM `category` ORDER BY `name`");
$sth->execute();
$category = $sth->fetchAll(PDO::FETCH_ASSOC);
$category = array_to_tree($category);
 
function array_to_tree($array, $sub = 0)
{
	$a = array();
	foreach($array as $v) {
		if($sub == $v['parent']) {
			$b = array_to_tree($array, $v['id']);
			if(!empty($b)) {
				$a[$v['id']] = $v;
				$a[$v['id']]['children'] = $b;
			} else {
				$a[$v['id']] = $v;
			}
		}
	}
	return $a;
}
 
function out_optgroup_options($array, $selected_id = 0, $level = 0) 
{
	$level++;
	$out = '';
	foreach ($array as $i => $row) {
		if (empty($row['children'])) {
			$out .= '<option value="' . $row['id'] . '"';
			if ($row['id'] == $selected_id) {
				$out .= ' selected';
			}
			$out .= '>';
			if ($level > 1) {
				$out .= str_repeat('#emsp;', $level - 1);
			}
			$out .= $row['name'] . '</option>';	
		} else {
			$out .= '<optgroup label="';
			if ($level > 1) {
				$out .= str_repeat('&mdash;', $level - 1);
			}
			$out .= $row['name'] . '"></optgroup>';
			$out .= out_optgroup_options($row['children'], $selected_id, $level);
		}
	}
	return $out;
}
?>
 
<select name="category">
	<option></option>
	<?php echo out_optgroup_options($category, 0); ?>
</select>

Результат: multilevel_select.rar

11.03.2023

109
A B i U S JS

PHP HTML CSS
Чат
    Для входа только имэйл или имя и апроль
    Можно сменить аватар
    Имэйл Ваше имя
    Пароль