Reordenar elementos en base de datos con Sortable jQuery UI

Una de las funciones más interesantes que nos permite jQuery UI es «Sortable», un widget que nos permite ordenar los elementos de una lista en HTML. Sin embargo, esto puede resultar más útil si además nos permite almacenar en algún sitio los cambios realizados, y esto es lo que vamos a hacer en este pequeño tutorial.

Para ello, vamos a utilizar HTML para la parte visual, a la que le podemos añadir CSS para darle un poco de estilo; JavaScript con jQuery y jQuery UI para la utilización del plugin; y PHP  y MySQL para el almacenamiento en la base de datos de los cambios realizados en la lista.

En principio deberíamos tener en un archivo con código HTML la lista que queramos ordenar, además de los archivos necesarios para el funcionamiento del plugin, por lo que tendríamos algo así:

<html>
<head>
<link href="src/js/jquery-ui-1.10.2.custom/css/smoothness/jquery-ui-1.10.2.custom.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script type="text/javascript" src="src/js/jquery-ui-1.10.2.custom/js/jquery-ui-1.10.2.custom.min.js"></script>
<title>Prueba de funcionamiento de Sortable jQuery UI</title>
</head>
<body>
<ul class="sortable">
	<li class="ui-state-default">Elemento 1</li>
	<li class="ui-state-default">Elemento 2</li>
	<li class="ui-state-default">Elemento 3</li>
</ul>

Las clases añadidas a los elementos de lista y los spans son pequeños diseños que nos da jQueryUI en su CSS por defecto (el que hemos añadido en el head). Por supuesto necesitamos enlazar correctamente a donde tengamos nuestros archivos de jQuery UI (.css y .js).

A continuación insertaríamos el código Javascript para el funcionamiento básico de Sortable jQuery UI, que es algo realmente sencillo:

<script type="text/javascript">
$(function() {
	$(".sortable").sortable();
	$( ".sortable" ).disableSelection();
});
</script>
</body>
</html>

Hemos aprovechado para cerrar el código HTML, por lo que tendríamos ya el archivo completo. El método disableSelection() hace que no podamos seleccionar el texto de dentro, haciendo mucho más fácil el arrastrar elementos.

Con esto ya podemos probar cómo funciona el widget Sortable, y ahora vamos a lo que realmente nos interesa, que es cómo tratar estos cambios:

En primer lugar debemos tener una base de datos para almacenar el orden, que podría tener, para simplificar, una tabla con una «id», «nombre» y «orden» con 3 elementos.

--
-- Estructura de tabla para la tabla `sortable`
--

CREATE TABLE IF NOT EXISTS `sortable` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`nombre` varchar(255) NOT NULL,
`orden` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ;

--
-- Volcado de datos para la tabla `sortable`
--

INSERT INTO `sortable` (`id`, `nombre`, `orden`) VALUES
(1, 'Elemento 1', 1),
(2, 'Elemento 2', 2),
(3, 'Elemento 3', 3);

Podríamos crear ahora un archivo PHP con las funciones necesarias para el manejo de la base de datos (aquí ya hemos pensado en el método que usaremos para ordenar los elementos más adelante). Le hemos llamado, por ejemplo, «sortable.php»:

<?php
function conectar() {
  	$server = 'localhost';
 	$user = 'tu usuario';
 	$pass = 'tu pass';
 	$db = 'tu base de datos';

 	$conexion = mysqli_connect($server, $user, $pass, $db);
	return $conexion;
}
function get_elementos() {
  	$conexion = conectar();

	$consulta = mysqli_query($conexion,
  		"
 		SELECT *
 		FROM sortable
 		ORDER BY orden ASC
 		");
 	if ($consulta) {
 		$res = array();
		while ($fila = mysqli_fetch_array($consulta)) {
			$res[] = $fila;
		}
		mysqli_close($conexion);
		return $res;
	}
	else {
		mysqli_close($conexion);
		return false;
	}
}
function reordenar($id, $orden) {
	$conexion = conectar();

	$consulta = mysqli_query($conexion, "
		UPDATE sortable
		SET orden = $orden
		WHERE id = $id");

	if ($consulta) return true;
	return false;
}
?>

De modo que ahora cargaríamos los datos dinámicamente en nuestro archivo HTML que pasaría a ser un archivo PHP con los siguientes cambios:

<ul class="sortable">
	<?php
	require_once('sortable.php');
	$elementos = get_elementos();
	foreach ($elementos as $elemento) {
	?>
	<li class="ui-state-default" id="elemento-<?php echo $elemento['id']; ?>">
 	 	<?php echo $elemento['nombre']; ?>
 	</li>
	<?php } ?>
</ul>

Como veis, hemos asignado una id única a cada elemento que coincide con la id guardada en la base de datos.

La API de jQuery UI es en general realmente extensa y útil, pero fijémonos en concreto en la de Sortable http://api.jqueryui.com/sortable/ donde en «Events» podemos encontrar «update». Es este evento el que se ejecuta al actualizar (terminar de arrastrar) un elemento de la lista, y por tanto, el que nos interesa. Para usarlo deberíamos sustituir la línea $(".sortable").sortable(); por lo siguiente:

$( ".sortable" ).sortable({
	update: function(event, ui) {
		$(".ajax-loader").show();
			var orden = $(this).sortable('toArray').toString();
			$.ajax({
				url: 'src/ajax/reordenar-slider.php',
				data: {"data": orden},
				type: 'post'
			}).done(function(data) {
		});
		$(".ajax-loader").hide();
	}
});

Nota: El objeto «ajax-loader» es opcional, sería un icono que nos mostraría un texto o el típico icono de «Cargando…» para saber cuándo está ejecutando la petición ajax y cuándo termina.

El método «toArray» de sortable nos devuelve un Array de jQuery con las ids de nuestra lista, que podemos transformar a un String de elementos separados por comas con el método toString (de jQuery).

A continuación debemos hacer una llamada ajax a un archivo .php encargado de reordenar este String que vamos a pasarle como parámetro. Esto hay varias maneras de hacerlo, pero en este caso hemos decidido pasárselo en un parámetro data usando el tipo de envío por «post».

Dicho archivo PHP que hemos decidido alojarlo en src/ajax/ contendría el siguiente código que actualizaría los ordenes de nuestros elementos:

<?php
require_once('../../sortable.php');
if (!empty($_POST['data'])) {
  	$data = $_POST['data'];
  	$orden = 1;
  	$array_elementos = explode(',', $data); // separamos por comas y guardamos en un array
 	foreach ($array_elementos as $elemento) {
 	 	// recordamos que los elementos se guardaban como "elemento-1", "elemento-2", etc
	 	$elemento_id = explode('-', $elemento); // en $elemento_id[1] tendríamos la id
 	 	$id = $elemento_id[1];
		reordenar_elemento($id, $orden); // reordenamos
 	 	$orden++; // aumentamos 1 al orden
 	}
} ?>

De este modo habríamos recorrido todos los elementos de la lista y los habríamos vuelto a ordenar, así que si actualizamos la página deberíamos verlos en el orden que los habíamos dejado la última vez.

Eso sería todo para poder recorrer los elementos de la lista y reordenarlos con el widget Sortable de jQuery UI.

Lo he hecho un poco de cabeza, así que si veis que algo falla no dudéis en comentar y a ver si arreglamos el problema.

2 comentarios

  1. COMENTARIOS ENVIADOS DESDE GOOGLE (Actualmente deshabilitados por comodidad)
    Toni Marrahí

    Hola, el ejemplo es correcto, valido para una tabla con pocos items. El problema que tiene esta solucion es que si la lista es muy grande y/o hay muchos usuarios reordenando la lista, el overhead para la bbdd es alto porque se hace un update por cada item de la lista (independientemente de si la lista tiene 3 o 10.000 items)
    Hay alternativas, como utilizar una lista enlazada (donde cada item tiene una referencia al item anterior y al posterior) o mejor todavia almacenar el orden en una sola fila. De este modo, da igual si la lista tiene 3 items que 10.000, solo se hara un update en lugar de 10.000, y la bbdd te lo agradecera.
    Un saludo

    + Respuesta de Javier:

    Efectivamente, esta solución la he utilizado para reordenar slides de una web, para noticias de un newsletter (20 a lo sumo), etc. No había tenido en cuenta esto que comentas, muchas gracias por el comentario!


    juan alfonso

    ah me di cuenta que no esta enviando el post de ajax nose porque..no tendria que abilitarle algo al servidor o algo asi?yo creo que no

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.