Сен 30

Порядковый номер элемента в списке или “выше->ниже”

Категория: Программирование

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

Рассмотрим 2 вида реализации функционала перемещения элементов по списку:

1) Выше, ниже;
2) указание индекса элемента в списке.

1.1 Выше ↑

Первый вариант хорош для использования на небольших кол-вах записей 20-30, но в нем есть своя прелесть, никаких проблем, о которых мы поговорим далее при использовании данного варианта не возникает. Алгоритм действий происходящих по нажатию кнопки “выше” изложен ниже.

  1. Проверить наличие элемента с таким id в базе данных и заодно захватить его индекс (select element_index from elements where element_id = 5);
  2. Если таковой существует, проверить не является ли он верхним в списке, то есть проверить наличие элемента с индексом меньше текущего. Предположим, что у данного элемента индекс - 25 (select element_id, element_index from elements where element_index < 25 limit 0,1). Так как элементов с индексом меньшим 25 может быть больше одного, используем limit;
  3. Если такового не существует, значит элемент самый верхний в списке, делаем вывод, что выше его поднять уже некуда;
  4. Если такой элемент существует (например индекс у него 15, а id = 3), используем данные выбранные из запроса:
  • Обновляем исходный элемент и устанавливаем ему индекс предыдущего (update elements set element_index = 15 where element_id = 5);
  • Обновляем предыдущий элемент и устанавливаем ему индекс исходного элемента (update elements set element_index = 25 where element_id = 3);

Теперь еще раз серия запросов к базе, реализующая функционал кнопки “выше”:

На входе у нас element_id = 5;


1) select element_index from elements where element_id = 5;
//получили element_index = 25;
2) select element_id, element_index from elements where element_index < 25;
//получили элемент с предыдущим индексом element_id = 3 и element_index = 15;
3) update elements set element_index = 15 where element_id = 5;
//устанавливаем входящему элементу индекс предыдущего;
4) update elements set element_index = 25 where element_id = 3;
//устанавливаем элементу с предыдущим индексом индекс входящего;

1.2 Ниже ↓

В данном случае делаем все то же самое, только используем вместо предыдущего элемента следующий.

2 Ручное указание индекса

Когда появляется желание указывать индекс вручную, начинаются проблемы, о которых я обещал рассказать выше. Если заказчик хочет, что бы индексы всегда находились в порядке возрастания, начиная от самого меньшего, этого можно достигнуть непростыми манипуляциями при помощи функций сортировок массива в PHP, в которых участвует ассоциативный массив с ключем = element_index и значением = element_id. Запускаем ksort по этому массиву, на выходе получаем массив отсортированный по индексам. Запускаем for и делаем update в базе по значениям ключей, то есть id-шникам, наращивая индексы начиная с 1. Что бы предусмотреть ввод человеком нескольких одинаковых индексов можно написать обработчик на javascript, но если этого делать не хочется можно обойтись следующим функционалом.

От массива полученного из POST в котором ключем является element_id а значением является element_index отделяем одномерный массив - массив индексов. Далее сортируем его sort(). Далее foreach по отсортированному массиву индексов и сопоставляем индексам id-шники  из исходного массива, после каждого сопоставления, индекс из исходного массива удаляем, дабы избежать дублирования.

1) Исходный массив

Исходный массив

2)  Отделенный массив индексов

Отделенный массив индексов

3)  Отсортированный массив

Отсортированный массив

4) Скомпонованный массив

Скомпонованный массив

После этих действий достаточно запустить foreach по финальному массиву и сделать автоинкрементную переменную, которая и будет заполнять значения индексов в порядке возрастания начиная с 1.


$ai = 1;
foreach ($arrResult as $element_index => $element_id) {

$updateSQL = “update elements set element_index = ” . $ai . ” where element_id = ” . $element_id;

$ai ++;

}

И не забываем сортировать выборку по полю element_index в нужном порядке.

2 комментариев на данный момент

  1. german Апрель 15th, 2008 08:23

    Или можно javascript’ом перетаскивать элементы (в script.aculo.us это Sortable в dragdrop.js, а в jQuery есть Sortables в jQuery.UI). Javascript после каждого перетаскивания формирует строку вида testlist[]=2&testlist[]=5&testlist[]=3&testlist[]=4&testlist[]=5&testlist[]=6&testlist[]=1&testlist[]=1&testlist[]=7&testlist[]=8
    и потом получается массив с id выстроенными в новом порядке. Вот и все (:

  2. vitamin Апрель 16th, 2008 00:27

    Отличный вариант, спасибо.
    Просто столкнулся с такой задачей впервые и не было времени делать ресерч по готовым решениям и прикручивать их, хотя о d&d-сортировке наслышан и видел как её юзали.

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