Jump to content
  • 0

Управление деревьями в MYSQL


rus
 Share

Question

Добрый день!

Кто-нибудь сталкивался с реализацией перемещения разделов вверх-вниз?

Возможно ли при такой структуре таблицы перемещать строки?

CREATE TABLE `pages` (
`id` int(11) NOT NULL auto_increment,
`parent_id` int(11) default '0',
`title` varchar(255) NOT NULL,
`title2` varchar(255) NOT NULL,
`full_text` text NOT NULL,
`meta_t` varchar(255) NOT NULL,
`meta_k` varchar(255) NOT NULL,
`meta_d` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=11 DEFAULT CHARSET=cp1251 AUTO_INCREMENT=11;

Link to comment
Share on other sites

13 answers to this question

Recommended Posts

  • 0
сортировать можно и по id, а перемещать как?

Для того чтобы перемещать можно было нужно ввести дополнительное числовое поле "pos"

Поле это должно работать так:

при добавлении первой записи его значение должно быть равно 1.

При добавлении последующей записи его значение должно вычисляться так: берем максимальную позицию в таблице и увеличиваем ее на 1.

При этом если при выводе задана прямая сортировка по полю pos, то новые записи будут помещаться в конец таблицы. Если обратная - то в начало.

Если нужно поднять запись (при прямой сортировке). То действуем так: извлекаем значение pos для этой записи, извлекаем значение поля pos для вышестоящей записи(у нее pos будет меньше) и меняем значения поля pos у этих записей местами.

Соответственно если нужно запись опустить, то действуем так же за тем исключением, что извлекаем нижестоящую запись (у нее значение pos будет больше).

Вот, например, функция, которой я пользуюсь:

Параметры:

$id_position - номер поднимаемой позиции

$tbl_name - название таблицы, в которой осуществляется подъем записи

$where - дополнительное where-условие, если оно требуется

$fld_name - название поля, в котором содержится первичный ключ

// Подъём блока на одну позицию вверх
function up($id_position, $tbl_name, $where = "", $fld_name = "id_position")
{

// Извлекаем текущую позицию
$query = "SELECT pos FROM $tbl_name
WHERE $fld_name = $id_position
LIMIT 1";
$pos = mysql_query($query);
if(!$pos)
{
throw new ExceptionMySQL(mysql_error(),
$query,
"Ошибка при извлечении
текущей позиции");
}

if(mysql_num_rows($pos))
{
$pos_current = mysql_result($pos, 0);
}
else
{
throw new ExceptionMySQL(mysql_error(),
$query,
"Ошибка при извлечении
текущей позиции");
}

// Извлекаем вышестоящую позицию
$query = "SELECT pos FROM $tbl_name
WHERE pos < $pos_current $where
ORDER BY pos DESC
LIMIT 1";
$pos = mysql_query($query);
if(!$pos)
{
throw new ExceptionMySQL(mysql_error(),
$query,
"Ошибка при извлечении
предыдущей позиции");
}

if(mysql_num_rows($pos))
{
$pos_preview = mysql_result($pos, 0);

// Меняем местами текущую и вышестоящую позиции
$query = "UPDATE $tbl_name
SET pos = $pos_current + $pos_preview - pos
WHERE pos IN ($pos_current, $pos_preview) $where";
if(!mysql_query($query))
{
throw new ExceptionMySQL(mysql_error(),
$query,
"Ошибка изменения
позиции");
}
}
}

P.S. вызов функции нужно поместить в try-обработчик и добавить класс ExceptionMySQL, который служит для вывода сообщения об ошибке, если произойдет исключительная ситуация(ExceptionMySQL наследуется от Exception).

Edited by alex_tihonko
Link to comment
Share on other sites

  • 0

s0rr0w а можешь если не на примере, то на словах объяснить?

мне принцип понять интересно, хотя если честно, теория меня мало интересует, так как я больше понимаю только на практике, а теория ни разу никогда не давалась, при чем в различном направлении, только практика наталкивает меня на понимание вещей, но тем не менее буду признателен за любую ценную информацию.

alex_tihonko - спасибо тебе огромное за столь развернутый ответ, буду разбирать твой пример. ;)

Иван Шумов - спасибо за ссылку, но я этот вариант уже смотрел и отмел его только по той причине, что у меня структура таблицы другая, а если ее менять, то надо менять и остальные запросы связанные с ней.

Link to comment
Share on other sites

  • 0

Пожалуйста! ;)

Если пример понравится, вот код обеих функции down(), up():

<?php
////////////////////////////////////////////////////////////
// 2005-2008 (C) Кузнецов М.В., Симдянов И.В.
// PHP. Практика создания Web-сайтов
// IT-студия SoftTime
// http://www.softtime.ru - портал по Web-программированию
// http://www.softtime.biz - коммерческие услуги
// http://www.softtime.mobi - мобильные проекты
// http://www.softtime.org - некоммерческие проекты
////////////////////////////////////////////////////////////
// Выставляем уровень обработки ошибок
// (http://www.softtime.ru/info/articlephp.php?id_article=23)
error_reporting(E_ALL & ~E_NOTICE);

// Опускание блока на одну позицию вниз
function down($id_position, $tbl_name, $where = "", $fld_name = "id_position")
{

// Извлекаем текущую позицию
$query = "SELECT pos FROM $tbl_name
WHERE $fld_name = $id_position
LIMIT 1";
$pos = mysql_query($query);
if(!$pos)
{
throw new ExceptionMySQL(mysql_error(),
$query,
"Ошибка при извлечении
текущей позиции");
}

if(mysql_num_rows($pos))
{
$pos_current = mysql_result($pos, 0);
}
else
{
throw new ExceptionMySQL(mysql_error(),
$query,
"Ошибка при извлечении
текущей позиции");
}

// Извлекаем следующую позицию
$query = "SELECT pos FROM $tbl_name
WHERE pos > $pos_current $where
ORDER BY pos
LIMIT 1";
$pos = mysql_query($query);
if(!$pos)
{
throw new ExceptionMySQL(mysql_error(),
$query,
"Ошибка при извлечении
следующей позиции");
}

if(mysql_num_rows($pos))
{
$pos_next = mysql_result($pos, 0);

// Меняем местами текущую и следующую позиции
$query = "UPDATE $tbl_name
SET pos = $pos_next + $pos_current - pos
WHERE pos IN ($pos_next, $pos_current) $where";
if(!mysql_query($query))
{
throw new ExceptionMySQL(mysql_error(),
$query,
"Ошибка изменения
позиции");
}
}
}

// Подъём блока на одну позицию вверх
function up($id_position, $tbl_name, $where = "", $fld_name = "id_position")
{

// Извлекаем текущую позицию
$query = "SELECT pos FROM $tbl_name
WHERE $fld_name = $id_position
LIMIT 1";
$pos = mysql_query($query);
if(!$pos)
{
throw new ExceptionMySQL(mysql_error(),
$query,
"Ошибка при извлечении
текущей позиции");
}

if(mysql_num_rows($pos))
{
$pos_current = mysql_result($pos, 0);
}
else
{
throw new ExceptionMySQL(mysql_error(),
$query,
"Ошибка при извлечении
текущей позиции");
}

// Извлекаем вышестоящую позицию
$query = "SELECT pos FROM $tbl_name
WHERE pos < $pos_current $where
ORDER BY pos DESC
LIMIT 1";
$pos = mysql_query($query);
if(!$pos)
{
throw new ExceptionMySQL(mysql_error(),
$query,
"Ошибка при извлечении
предыдущей позиции");
}

if(mysql_num_rows($pos))
{
$pos_preview = mysql_result($pos, 0);

// Меняем местами текущую и вышестоящую позиции
$query = "UPDATE $tbl_name
SET pos = $pos_current + $pos_preview - pos
WHERE pos IN ($pos_current, $pos_preview) $where";
if(!mysql_query($query))
{
throw new ExceptionMySQL(mysql_error(),
$query,
"Ошибка изменения
позиции");
}
}
}

?>

Копирайты только нужно оставлять. Это не мое личное изобретение :), а изобретение руководителей студии www.softtime.ru

У них очень много полезных скриптов/приложений. Даже FrameWork свой есть. Я на базе него сайты делаю. Весьма удобный, правда функционал у него не очень большой. Но для маленьких/средних проектов вполне подойдет.

Link to comment
Share on other sites

  • 0

Обясню как делаю я.

Для любой таблицы с древовидной логикой у меня есть три обязательных поля: `id`, `parent` и `ord`- айдишник, родительский айдишник и порядок внутри родителя. `ord` может принимать значения 10, 20, 30 и т.д. При выводе какого-либо из разделов делаю для каждого из элементов поле с указанием текущего порядка примерно таким образом:

Делаем запрос

SELECT * FROM `table` WHERE `parent`=5 ORDER BY `ord` ASC

и далее циклом выводим нужные нам значения

$name <input type="text" name="ord[$id]" value="$ord">

Порядок можем менять как нам угодно. Я именно поэтому и делаю значения порядка дискретными через десятку, чтобы для небольших перемещений менять минимум цифр. После сабмита формы делаем примерно следующее:

asort($_POST['ord']);
$s=10;
foreach($_POST['ord'] as $key=>$value)
{
mysql_query("UPDATE `table` SET `ord`='{$s}' WHERE `id`='{$key}'");
$s+=10;
}

Вариант, конечно, не идеальный, но для небольших таблиц вполне сойдёт.

Link to comment
Share on other sites

  • 0

Veseloff спасибо за свой пример.

alex_tihonko еще раз спасибо, копирайт я не буду оставлять, так как мне нужно было просто подсмотреть всего лишь как это делается и на основе это сделать по-своему, ведь дальше то мне с этим работать, а просто копипастить это не мой удел. ;)

З.Ы. теперь хоть есть от чего оттолкнуться, буду пробовать все варианты, возможно свой сделаю на основе ваших.

Link to comment
Share on other sites

  • 0

Чтобы понимать позиционирование элементов в деревьях, нужно сначала нарисовать дерево.

Нужно помнить, что программист работает не с идеальными данными, в процессе работы различных алгоритмов могут возникать всевозможные ситуации, которые желательно предусмотреть.

Проще всего строить деревья при помощи id -> parent_id. Это простой, но и самый несовершенный способ построения деревьев.

Проблем у него масса. Например, чтобы выбрать всех соседей у какого-то элемента и всех парентов у него-же, одним запросом к базе не отделаешься.

Возьмем простейшее дерево

1[0]:   Item 1
* 3[1]: Item 1.1
* 5[1]: Item 1.2
* 11[1]: Item 1.3
4[0]: Item 2
33[0]: Item 3
* 45[33]: Item 3.1

Первое число - идентификатор ноды. Второе - идентификатор родительской ноды.

Но, чтобы отсортировать дочерние элементы первого элемента, нужно ввести дополнительное поле position.

 * 3[1]{0}: Item 1.1
* 5[1]{1}: Item 1.2
* 11[1]{2}: Item 1.3

При запросе списка дочерних нод первого элемента мы должны выбрать все ноды, у которых parent_id = 1, и отсортировать по полю position

Изменяя позицию элементов, например на такую

 * 3[1]{2}: Item 1.1
* 5[1]{0}: Item 1.2
* 11[1]{1}: Item 1.3

На выходе мы получим

Item 1.2

Item 1.3

Item 1.1

Недостатком данного метода является то, что, например, при переносе позиции всего одной дочерней ноды, нужно обновить значения у всех дочерних элементов.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Answer this question...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. See more about our Guidelines and Privacy Policy