Занимаясь backend разработкой, каждый программист сталкивается с тем что любую древовидную структуру - будь то меню или список категорий нужно выводить на сайте не обычным выпадающим списком, а красиво оформленным, что бы пользователю было удобно пользоваться вашей CMS!
Можно красиво форматировать вывод блоков, делать отступы дочерних разделов, но упорядочить разделы будет все равно не удобно. В помощь приходит мощный jQuery плагин Nestable.
Скачать nestable его вы можете на сайте разработчика http://dbushell.github.io/Nestable/
Приступим к созданию древовидного меню сортируемого drag and drop:
База данных будем иметь стандартную структуру. id, name, parent_id а также ячейку order для значений сортировки.

Итак сделаем выборку всех родительских категорий и поместим результат в массив:
$sql = "SELECT * FROM menu WHERE parent_id='0' ORDER BY order"; $result = mysql_query($sql, $connection); $numRows = mysql_num_rows($result);
Теперь выведем на страницу блок с родительскими элементами
echo "\n"; echo "\n\n"; // Блок должен быть здесь не перемещайте его echo "\n";\n\n"; echo "\n"; echo "\n"; while($row = mysql_fetch_array($result)) { echo "\n"; echo "
\n\n"; echo "- "; echo "
\n"; } echo "{$row['id']}: {$row['name']}"; menu_showNested($row['id']); echo "
Наверное вы заметили функцию menu_showNested ? Сейчас мы будем использовать рекурсивную функцию для вывода всех дочерних категорий.
function menu_showNested($parentID) {
$sql = "SELECT * FROM menu WHERE parent_id='$parentID' ORDER BY order";
$result = mysql_query($sql, $connection);
$numRows = mysql_num_rows($result);
if ($numRows > 0) {
echo "\n";
echo "\n";
while($row = mysql_fetch_array($result)) {
echo "\n";
echo "- \n";
echo "{$row['id']}: {$row['name']}\n";
menu_showNested($row['id']);
echo "
\n";
}
echo "
\n";
}
}
Все меню мы вывели.
Теперь самое сложное - создание и передача массива в формате json обработчику на сервере и сохранение данных в бд
//========================================== nestable script
function lagXHRobjekt() {
var XHRobjekt = null;
try {
ajaxRequest = new XMLHttpRequest(); // Firefox, Opera, ...
} catch(err1) {
try {
ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP"); // Noen IE v.
} catch(err2) {
try {
ajaxRequest = new ActiveXObject("Msxml2.XMLHTTP"); // Noen IE v.
} catch(err3) {
ajaxRequest = false;
}
}
}
return ajaxRequest;
}
//функция отправки методом post массива с порядком в формате json
function menu_updatesort(jsonstring) {
mittXHRobjekt = lagXHRobjekt();
if (mittXHRobjekt) {
mittXHRobjekt.onreadystatechange = function() {
if(ajaxRequest.readyState == 4){
var ajaxDisplay = document.getElementById('sortDBfeedback');
ajaxDisplay.innerHTML = ajaxRequest.responseText;
} else {
// закомментируйте эту строку если не хотите выводить спинер загрузки
document.getElementById('sortDBfeedback').innerHTML = "
";
}
}
var tosend = "jsonstring="+jsonstring;
ajaxRequest.open("POST","/admin/menu/menu_sortable_save/",true);
ajaxRequest.setRequestHeader("Content-type","application/x-www-form-urlencoded");
ajaxRequest.send(tosend);
}
}
$(document).ready(function()
{
//функция обновления позиций меню
var updateOutput = function(e)
{
var list = e.length ? e : $(e.target),
output = list.data('output');
if (window.JSON) {
output.val(window.JSON.stringify(list.nestable('serialize')));//, null, 2));
menu_updatesort(window.JSON.stringify(list.nestable('serialize')));
} else {
output.val('JSON browser support required for this demo.');
}
};
// вызываем функцию для записи изменения порядка меню в бд.
$('#nestableMenu').nestable({group: 1}).on('change', updateOutput);
// вывод начальной последовательности если элемент существует на странице
var variable = $('#nestableMenu').data();
if ( typeof variable !== "undefined" && variable) {
updateOutput($('#nestableMenu').data('output', $('#nestableMenu-output')));
}
//кнопки развернуть светнуть меню
$('#nestable-menu').on('click', function(e)
{
var target = $(e.target),
action = target.data('action');
if (action === 'expand-all') {
$('.dd').nestable('expandAll');
}
if (action === 'collapse-all') {
$('.dd').nestable('collapseAll');
}
});
//fix позволяющий кликать на ссылки в nestable
$(".dd a").on("mousedown", function(event) { // mousedown prevent nestable click
event.preventDefault();
return false;
});
$(".dd a").on("click", function(event) { // click event
event.preventDefault();
window.location = $(this).attr("href");
return false;
});
});
Как видно ничего сложного, я подробней остановлюсь на последних строках! Дело в том что наше меню использует технологию drag and drop следовательно для вызова любой ссылки с блока нашего меню приходится предотвращать выполнение функции drag and drop и выполнять действие перехода по ссылке.
Вот скриншот пример древовидного меню 
Теперь самое важное - это серверная часть нашего плагина, где собственно происходит сохранение, обработка и парсинг всех данных.
Следующий код показывает методы класса Menu, контроллера admin и адаптирован для фреймворка codeigniter, по просьбе могу выложить полностью работоспособный вариант этого плагина для codeigniter :
//сохранить порядок меню nestable
function menu_sortable_save(){
if ($this->input->post()) {
$jsonstring = $this->input->post('jsonstring');
// Декодируем в массив
$jsonDecoded = json_decode($jsonstring, true, 64);
function parseJsonArray($jsonArray, $parentID = 0)
{
$return = array();
foreach ($jsonArray as $subArray) {
$returnSubSubArray = array();
if (isset($subArray['children'])) {
$returnSubSubArray = parseJsonArray($subArray['children'], $subArray['id']);
}
$return[] = array('id' => $subArray['id'], 'parentID' => $parentID);
$return = array_merge($return, $returnSubSubArray);
}
return $return;
}
$readbleArray = parseJsonArray($jsonDecoded);
// циклом проходимся по массиву и сохраняем в бд
foreach ($readbleArray as $key => $value) {
if (is_array($value)) {
$data = array(
'order' => $key,
'parent_id' => $value['parentID']
);
$this->db->where('id', $value['id']);
$this->db->update('menu', $data) or $this->admin_lib->set_admin_alerts('alert_danger', 'Системное сообщение - Ошибка при сортировке записей', 'admin/menu/menu_list_sortable');;
}
}
// вывод сообщения на страницу
echo "Порядок категорий успешно сохранен в " . date("y-m-d H:i:s") . "!";
}
}
Итак, мы получили отличный плагин для вывода древовидного меню средствами jQuery PHP и MySQL. Результат:

Как и обещал исходники приложения и полные исходники модуля для codeigniter
Mostak Shahid
how to use полные исходники модуля для codeigniter.
Евгений Поляков
MOSTAK SHAHID, исходники модуля - предполагают что вы используете свой движок на codeigniter с расширением hmvc. Просто скопировать папку и установить - не получится. Нужно разобраться в исходном коде и тогда можно использовать его по своему усмотрению. В данный момент уже есть три статьи по созданию движка на codeigniter 3 + HMVC. Этот модуль как раз будем создавать и разбирать код в одной из ближайших статей.