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

Примеры построения многоуровневых выпадающих списков (select option) и базы данных с применением рекурсии PHP.

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

MySQL-таблица `category`

category.mysql

1

Обычный пробел в начале текста элемента <option>...<option> не выводится, поэтому нужно использовать мнемонику &emsp; – широкий пробел.

  1. В начале получаем все записи из БД в виде ассоциативного массива.
  2. С помощью функции array_to_tree() преобразуем его в древовидный, к элементам массива добавляется элемент «children» в который перемещаются все дочерние элементы.
  3. С помощью функции out_options() рекурсивно выводятся все элементы массива.
  4. Во втором аргументе функции 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>
HTML

Результат:

2

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

<?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>
PHP

Результат:

3

Использование <optgroup label="">...</optgroup> оправдано если необходимо выбрать только крайнюю категорию в дереве, но 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>
HTML

Результат:

05.12.2021, обновлено 21.03.2022
11991
Предыдущая запись Select c поиском

Комментарии 1

Кирилл Трубецкой Кирилл Трубецкой
6 декабря 2021 в 02:47
А как сделать так:
если выбираешь родителя, то появляется следующий select с его дочерним элементом и так пока дочерний элемент не будет конечным?

, чтобы добавить комментарий.

Другие публикации

Селект с чекбоксами
Селект с множественным выбором (select multiple) весьма не удобен, при выборе часто забываешь нажимать сtrl и все сбрасывается. В место него можно использовать чекбоксы в выпадающем списке.
36689
+10
Работа с select с помощью JQuery
Сборник методов JQuery для работы с выпадающими списками select option с примерами.
245140
+13
Примеры использования PDO MySQL
В статье приведены основные примеры работы с расширением PHP PDO. Такие как подключение к БД, получение, изменение и...
104301
+8
MySQL SELECT на примере каталога товаров
Несколько примеров SQL-запросов для интернет магазина или каталога товаров.
13545
+2
Select c поиском
Выпадающий список с большим количеством опций крайне неудобен, чтобы облегчить выбор, в селект можно добавить поиск с помощью плагинов JQuery. Рассмотрим основные из них.
57492
+16
Select option с ссылками
По спецификации HTML в option нельзя вставить ссылку, если все-таки её туда вставить, то работать она не будет. В таких случаях можно сделать перенаправление браузера по клику на JS или имитацию...
24759
+9