jQuery + плагины: сортировка и редактирование списка (часть вторая)

Владимир | | Ajax, HTML, JavaScript, PHP, Web разработка.

jquery sortable

В прошлой статье я начал рассказывать о создании редактируемого html списка с возможностью сортировки записей. Мы рассмотрели структуру БД, создали страницу со списком, подключили JS скрипты и разобрали добавление записей.

Осталось немного доделать 😉

Демо-версия находится здесь

Demo

Также вы можете скачать архив с этим примером

Source

Удаление существующих записей

Рассмотрим серверную часть removenote.php.

try {
	if (!isset($_POST['id'])) {
		throw new Exception('Не указан id записи');
	}
	//подключаемся к базе
	require_once('db.php');
	//удаляем запись (id записи приходит в формате note_id, приставку note_ вырезаем)
	$dbh->exec('DELETE FROM notes WHERE id='.$dbh->quote(substr($_POST['id'], 5)));
	$result = array(); 
	$result['id'] = $_POST['id'];
	//отправляет отчет браузеру
	echo json_encode($result);
} catch(Exception $e) {
	echo json_encode(array('err'=>'Ошибка: '.$e->getMessage()));
}

Для удаления записи нам нужно знать только её id. После того, как он получен, выполняем запрос на удаление.

Клиентская часть немного сложнее.

Ссылкам «Удалить» назначается обработчик события click.

.click(removeLi)

function removeLi() {
	//определяем id текущей записи
	var noteId = $(this).parent().attr('id');
	//запрашиваем подтверждение
	if (confirm('Точно удалить?')) {
		//отправляем запрос
		$.post('removenote.php', {'id':noteId}, function(data) {
			var response = eval('('+data+')');
			if (response['err'] != null) {
				alert(response['err']);
			} else {
				$('#' + response['id']).detach();
				//проверяем, остались ли записи в списке
				var notes = $('#sortable li');
				if (notes.length == 0) {
					//удаляем сам список и форму сортировки записей
					$('#sortable').detach();
					$('#changeOrder').detach();
					//добавляем сообщение "Записи отсутствуют"
					$('<h2>Записи отстутствуют</h2>').insertAfter('#addNewNote');
				}
			}
		});
	}
	return false;
}

Этот обработчик определяет id тега li, в котором расположена ссылка, запрашивает подтверждение удаления и отправляет ajax запрос серверу.

После получения ответа от сервера, он удаляет запись из списка с помощью метода .detach() и проверяет, остались ли в списке другие записи. Если не осталось – список вместе с формой сортировки записей также удаляется.

Изменение текста записей

Как и раньше, начинаем с серверной части. Скрипт update.php.

try {
	//проверяем, пришли данные или нет
	if (!isset($_POST['value'])
			|| '' == $_POST['value']
			|| !isset($_POST['id'])
			|| '' == $_POST['id']) {
		throw new Exception('Не указаны данные записи');
	}
	//подключаемся к базе
	require_once('db.php');
	//защита от XSS
	$note = htmlspecialchars($_POST['value']);
	//формируем запрос на изменение записи...
	$stmt = $dbh->prepare('UPDATE notes SET note=:note WHERE id=:id');
	//... и выполняем его
	$stmt->execute(array(':note'=>$note, ':id'=>substr($_POST['id'], 2)));
	$dbh = null;
	//тут мы не используем JSON формат, т.к. это требование плагина jeditable
	echo $note;
} catch(Exception $e) {
	echo 'Ошибка: '.$e->getMessage();
}

От браузера скрипт получает id записи и новый текст.

После выполнения обычных проверок, мы подключаемся к БД и отправляем запрос на изменение записи. При этом с помощью функции htmlspecialchars мы заменяем теги на их эскейп последовательности (защита от XSS). Защита от SQL Injection выполняется с помощью стандартных средств PDO.

Обратите внимание, что в этом случае мы отправляем ответ браузеру в виде обычного текста. Это требование плагина jeditable.

Клиентская часть.

Чтобы сделать текст записей редактируемым, мы для каждого тега span (который находится внутри li) выполняем метод editable.

var editableOptions = {
	indicator : 'Сохраняю...',
	tooltip : 'Сделайте двойной клик, чтобы изменить текст',
	event : 'dblclick',
	cancel : 'Отмена',
	submit : 'Сохранить'
};

$('span.note').editable('update.php', editableOptions);

В параметрах этого метода передаём адрес скрипта, который выполняет обновление записи на сервере, и объект editableOptions, который содержит настройки плагина. В данном случае мы указываем (параметр event), что плагин должен вставить форму редактирования при двойном клике по тексту. Одинарный клик, который используется по-умолчанию, нам не подходит, т.к. в этом случае форма будет появляться при каждой попытке перемещения записи.

Кроме события (dblclick) которое открывает форму редактирования, указанного в параметре event, для настройки плагина мы указали только надписи на кнопках и текст подсказок.

Запрос серверу плагин отправляет сам, после клика по кнопке «Сохранить».

Изменение порядка записей

Самая интересная часть 🙂

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

{
	'id':'id записи',
	'order': порядковый_номер_записи_в_отсортированном_списке
}

Затем этот массив преобразовывается в форма JSON и отправляется серверу.

Примечание. Для преобразования в формат JSON используется плагин jquery-json.

Выглядит клиентская часть следующим образом.

function updateOrder() {
	var notes = $('ul#sortable li');
	//проверяем количество записей в списке
	if (notes.length > 0) {
		var order = [];
		//формируем массив
		//просматриваем список в обратном порядке, т.к. записи выводятся в порядке
		//убывания значений в поле note_order (это позволяет при создании записи
		//присваивать ей максимальный порядковый номер и размещать в начале списка).
		$.each(notes.get().reverse(), function(index, value) {
			order.push({'id': $(value).attr('id'), 'order': index});
		});
		//отправка запроса (метод toJSON добавлен плагином json-2.2)
		$.post('changeorder.php', {'neworder':$.toJSON(order)}, function(data) {
			var response = eval('('+data+')');
			if (response.status == 'OK') {
				$('ul#sortable').effect("highlight", {}, 3000);
			}
			else {
				alert(response.mes);
			}
		});
	}
	return false;
}

Обратите внимание, мы обрабатываем массив с элементами списка в обратном порядке (используется метод .reverse()). Это сделано потому, что записи выводятся в порядке убывания значений в поле note_order. Как я объяснял ранее, такой подход позволяет упростить определение номеров сортировки при создании новых записей.

Если данные успешно сохранены, подсвечиваем список, в противном случае – выводим сообщение об ошибке.

Серверный скрипт (changeorder.php).

try {
	//проверяем полученные данные
	if (!isset($_POST['neworder']) || count($_POST['neworder']) > 5000) {
		throw new Exception('Недопустимые данные');
	}
	//подключаемся к БД
	require_once('db.php');
	//создаём запрос на обновление
	$stmt = $dbh->prepare('UPDATE notes SET note_order=:note_order WHERE id=:id');
	//преобразовываем строку в JSON формате в массив объектов
	$data = json_decode($_POST['neworder']);
	if (null == $data) {
		throw new Exception('Недопустимый формат');
	}
	//обновляем записи
	foreach ($data as $note) {
		$stmt->execute(array(':note_order'=>$note->order, ':id'=>substr($note->id, 5)));
	}
	$dbh = null;
	//отправляем отчет браузеру
	echo json_encode(array('status'=>'OK'));
} catch(Exception $e) {
	echo json_encode(array('status'=>'ERR', 'mes'=>'Ошибка: '.$e->getMessage()));
}

Здесь мы проверяем полученные данные и в цикле обновляем все записи. Используется обычный UPDATE запрос. Преобразование из JSON формата в PHP массив выполняется с помощью функции json_decode.

Как видите, пример получился довольно объёмный, но большая часть кода относится к различным проверкам. Поэтому, надеюсь, общую идею вы поняли 😉

Если у вас будут вопросы или замечания, пишите, буду рад обсудить!

  • Добрый день!
    Материал интересный,но есть вопрос.
    Сколько не читал материалов про сортировку используя jqueru ui sortable,не могу понять, где адекватно использовать?
    Пользователь же сам не будет заниматся перетаскиванием елементов списка.)
    P.S. Жду ответа на мой вопрос

    • Например, для WP есть плагин, который позволяет сортировать список категорий, там используется именно такая схема.

      • Владимир, а в других CMS, в частности в Data Life Engine, не знаете как его применить?

        • С DLE я не работал, но, думаю, принцип тотже, что и везде. Добавить на страницу список, подключить js скрипты (обычно для этого есть специальные функции, но в крайнем случае, можно вставить в теги script в файлы шаблона).

    • Nik

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

  • Добрый день!
    Материал интересный,но есть вопрос.
    Сколько не читал материалов про сортировку используя jqueru ui sortable,не могу понять, где адекватно использовать?
    Пользователь же сам не будет заниматся перетаскиванием елементов списка.)
    P.S. Жду ответа на мой вопрос

    • Например, для WP есть плагин, который позволяет сортировать список категорий, там используется именно такая схема.

      • Владимир, а в других CMS, в частности в Data Life Engine, не знаете как его применить?

        • С DLE я не работал, но, думаю, принцип тотже, что и везде. Добавить на страницу список, подключить js скрипты (обычно для этого есть специальные функции, но в крайнем случае, можно вставить в теги script в файлы шаблона).

  • ага спасибо,буду знать

  • ага спасибо,буду знать

  • Сергей

    наконец то я дождался 🙂 Большое спасибо за урок !!!

  • Сергей

    наконец то я дождался 🙂 Большое спасибо за урок !!!

  • Сергей

    вообще зачем такие вещи нужны ?? — для админки .. бывает что некоторе вещи выводятся через цикл, можно поменять местами. Для обучния хороший урок, я только разочаровался что он не на mysql, а на sqllite.

    • В этом примере я использовал библиотеку PDO, поэтому вы можете легко заменить БД на MySQL.
      Откройте файл db.php и измените параметры подключения

      $dns = new PDO(«mysql:host=localhost;dbname=название_базы», имя_пользователя, пароль);

  • Сергей

    вообще зачем такие вещи нужны ?? — для админки .. бывает что некоторе вещи выводятся через цикл, можно поменять местами. Для обучния хороший урок, я только разочаровался что он не на mysql, а на sqllite.

    • В этом примере я использовал библиотеку PDO, поэтому вы можете легко заменить БД на MySQL.
      Откройте файл db.php и измените параметры подключения

      $dns = new PDO(«mysql:host=localhost;dbname=название_базы», имя_пользователя, пароль);

  • Michael

    Очень нужная вещь, в закладки.

  • Michael

    Очень нужная вещь, в закладки.

  • AntonYu

    Владимир, а почему вы htmlspecialchars не перед выводом непосредственно делаете, чтобы в БД записывалась информация нетронутая?

    • Не совсем понял.
      Сторка 12
      $note = htmlspecialchars($_POST['value']);
      затем в строках 14 и 16
      $stmt = $dbh->prepare('UPDATE notes SET note=:note WHERE id=:id');
      $stmt->execute(array(':note'=>$note, ':id'=>substr($_POST['id'], 2)));
      т.е. в базу пойдут данные с конвертированными специальными символами HTML.

      • AntonYu

        Да, согласен. Почему вы их перед записью в БД конвертируете, а не перед выводом?

      • AntonYu

        echo htnlspecialchars($value);

        • А зачем в базе потенциально опасные записи?
          Серверу они вреда не нанесут, но в теории эти же данные может использовать другой скрипт, и там можно просто забыть вызвать htmlspecialchars.

  • AntonYu

    Владимир, а почему вы htmlspecialchars не перед выводом непосредственно делаете, чтобы в БД записывалась информация нетронутая?

    • Не совсем понял.
      Сторка 12
      $note = htmlspecialchars($_POST['value']);
      затем в строках 14 и 16
      $stmt = $dbh->prepare('UPDATE notes SET note=:note WHERE id=:id');
      $stmt->execute(array(':note'=>$note, ':id'=>substr($_POST['id'], 2)));
      т.е. в базу пойдут данные с конвертированными специальными символами HTML.

      • AntonYu

        Да, согласен. Почему вы их перед записью в БД конвертируете, а не перед выводом?

      • AntonYu

        echo htnlspecialchars($value);

        • А зачем в базе потенциально опасные записи?
          Серверу они вреда не нанесут, но в теории эти же данные может использовать другой скрипт, и там можно просто забыть вызвать htmlspecialchars.

  • Очень любопытный урок! Скачал, может где-нибудь пригодится такая сортировочка! Спасибо!

  • Очень любопытный урок! Скачал, может где-нибудь пригодится такая сортировочка! Спасибо!

  • Мне примерчик ваш очень понравился! Работает все очень хорошо!

  • Мне примерчик ваш очень понравился! Работает все очень хорошо!

  • А без базы данных такое реализовать никак нельзя? Пример очень любопытный!

    • Можно хранить данные в браузере, если он поддерживает какое-нибудь хранилище, но на сервере при этом ничего сохранено не будет.
      Так что БД — обязательный компонент (ее можно заметить файлами, но смысл будет тот же).

  • А без базы данных такое реализовать никак нельзя? Пример очень любопытный!

    • Можно хранить данные в браузере, если он поддерживает какое-нибудь хранилище, но на сервере при этом ничего сохранено не будет.
      Так что БД — обязательный компонент (ее можно заметить файлами, но смысл будет тот же).

  • 1_and_0

    Вообще непонятно откуда берется массив $_POST['neworder'], он нигде не объявлен в index.php, как скрипт его будет обрабатывать? сломал голову уже…, поднял на локальной машине базу и скрипты, ничего не работат

    • В строке 14 предпоследнего листинга формируется ajax запрос. Он и отправляет параметр 'neworder' методом POST. В нем находится массив order, преобразованный в json формат.

  • VLOmper

    Здравствуйте, у меня такая проблема:
    Ошибка: could not find driver. Что это означает? То что надо php 5.1 и выше или..?

    • Похоже на проблему с базой данных. Попробуйте запустить какой-нибудь скрипт, который просто подключается к базе.
      И проверьте в конфиге php подключены ли драйвера для работы с базой.

      Нужны такие модули

      extension=php_mysql.dll
      extension=php_mysqli.dll
      extension=php_pdo.dll
      extension=php_pdo_mysql.dll

  • blr_tromax

    Объясните пожалуйста вот эту часть вашего кода:

    $.each(notes.get().reverse(), function(index, value) { order.push({'id': $(value).attr('id'), 'order': index}); }); //отправка запроса (метод toJSON добавлен плагином json-2.2) $.post('changeorder.php', {'neworder':$.toJSON(order)}, function(data) { var response = eval('('+data+')'); if (response.status == 'OK') { $('ul#sortable').effect(«highlight», {}, 3000); } else { alert(response.mes); } });

    что здесь делается?

    • Перебираем все элементы массива notes в обратном порядке. Для каждого из них вызываем функцию, которая добавляет id элемента и его номер сортировки (order) в массив orders.
      Затем отправляем массив orders на сервер с помощью ajax запроса. Если сервер ответил «ОК», т.е. нормально сохранил новый порядок сортировки — подсвечиваем список, если нет — показываем сообщение об ошибке.

  • Отличный материал. Если я буду использовать данный скрипт для создания категорий, подкатегорий и т.д. Вопрос: например пользователь создал категории в них подкатегории, затем захотел удалить категорию в которой находятся подкатегории. Как ему запретить это сделать?

    • Для хранения категорий в базе данных у вас, скорее всего, будет таблица со следующими полями.
      id — первичный ключ
      name — название категории
      parent_id — id родительской категории

      Если приходит запрос на удаление категории с id=5, выполните проверку наличия дочерних категорий, например, так:

      SELECT * FROM categories WHERE parent_id=5

      Если запрос найдет строки, значит есть дочерние категории и удалять текущую не нужно.

  • Максим

    Были проблемы с базой данных и кодировкой наконец то мне удалось их решить. Я хочу сделать три сортируемых списка на одной странице подскажите, пожалуйста, как это сделать? И еще вопрос — все файлы php получиться в один объединить?

    • 1) Присвойте каждому списку уникальный атрибут id и передавайте его на сервер при сохранении сортировки.

      2) Да, получится.

  • Nik

    Принцип понятен. Автору спасибо!
    Кому нужен пример попроще: http://vredniy.ru/2010/04/jqueryui-sortable/

  • Nik

    Автор, вот зачем удалять комментарии, которые могут помочь соискателям? Данный скрипт в нормальном состоянии и понятным даже для новичка не так уж просто найти… поэтому постим еще раз ссылку на подобный скрипт, но проще в реализации(меньше кода):
    http://vredniy.ru/2010/04/jqueryui-sortable/

    Либо делайте свои статьи подобно-просто!

    • Я ничего не удалял. Оба ваши комментария опубликованы и в спам не попадали.