Búsqueda instantánea con AJAX, PHP y MySQL

Hoy traigo un tutorial donde mostraré una manera de obtener y mostrar datos de una base de datos MySQL con PHP y AJAX sin necesidad de recargar la página. En la búsqueda haremos una consulta a la BBDD para que sea case-insensitive y pueda concatenar varias columnas.

Ejemplo del resultado y sus peticiones al servidor

¿Cómo crear una búsqueda instantánea?

Esta vez haré el desarrollo en local, por cuestiones de comodidad. Utilizaré XAMPP (Apache y MySQL con PHPMyAdmin) en Windows.

1 de 5: HTML

Lo primero que necesitamos es «un lienzo en blanco» en PHP, al cual llamaremos index.php (por ejemplo).

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Documento sin título</title>
</head>

<body>
</body>
</html>

Después agregamos un formulario que acepte UTF-8 y con método POST. En el form debe haber un input del tipo texto (o búsqueda (search)) con nombre e ID «busqueda». Opcionalmente se le puede añadir un valor y placeholder vacío, un máximo de 30 caracteres y el auto-completado apagado, además de un texto arriba del formulario.

Debajo de este vamos a crear un contenedor donde se muestren los resultados de la búsqueda:

<form accept-charset="utf-8" method="POST">
<input type="text" name="busqueda" id="busqueda" value="" placeholder="" maxlength="30" autocomplete="off" />
</form>
<div id="resultadoBusqueda"></div>

2 de 5: jQuery

A continuación hay que agregar la librería de jQuery en el head del HTML para poder usar la función POST. Yo utilizaré la versión 1.9.1 minificada que tengo descargada.

<script src="js/jquery-1.9.1.min.js"></script>

Ahora debemos crear una función, donde habrá una variable que sea el valor del input en el formulario. Yo llamaré a la función buscar.

<script>
function buscar() {
    var textoBusqueda = $("input#busqueda").val();
};
</script>

Entonces creamos una condicional donde, si el input está vacío, no haga nada (o sea, que no modifique el contenedor resultadoBusqueda)  y, sino está vacío el input, la función POST de jQuery mostrará el resultado. Esta función toma los datos de la siguiente forma:

$.post( url [, data ] [, success ] [, dataType ] )

Puedes obtener más información al respecto desde la web oficial de jQuery (inglés).

Así que la función es la siguiente:

<script>
function buscar() {
    var textoBusqueda = $("input#busqueda").val();
	
    if (textoBusqueda != "") {
        $.post("buscar.php", {valorBusqueda: textoBusqueda}, function(mensaje) {
            $("#resultadoBusqueda").html(mensaje);
        }); 
    } else { 
        ("#resultadoBusqueda").html('');
	};
};
</script>

La estructura del $.post de jQuery en esas líneas funciona de la siguiente manera:

  1. «buscar.php» -> Toma la URL donde estará el PHP que va a devolver los resultados de la consulta a MySQL (eso lo haremos un poco más adelante).
  2. valorBusqueda: textoBusqueda -> Donde «valorBusqueda» es el nombre del POST que le daremos en PHP ( $_POST[‘valorBusqueda’] ) para obtener lo que hay en «textoBusqueda», que es el ID del input en el formulario.
  3. function(mensaje) -> Crea una función que devolverá el mensaje (el cual no será más que un echo de PHP) en el contenedor con un ID resultadoBusqueda (es decir: success).
  4. $(«#resultadoBusqueda»).html(mensaje) -> En el contenedor resultadoBusqueda se mostrará ese echo en PHP.

Para mostrar un mensaje en el contenedor resultadoBusqueda CUANDO SE VACÍE EL INPUT solo hay que modificar la siguiente linea del script:

$("#resultadoBusqueda").html('');

Y poner esto (por ejemplo):

$("#resultadoBusqueda").html('<p>JQUERY VACIO</p>');

Para que cuando cargue la página por primera vez (en cada visita) muestre un mensaje en el contenedor, debemos declarar que, cuando el documento haya cargado (y esté listo), mostrará un mensaje usando el método HTML de jQuery.

$(document).ready(function() {
    $("#resultadoBusqueda").html('<p>JQUERY VACIO</p>');
});

Por lo tanto, el script final sería algo como esto:

<script>
$(document).ready(function() {
    $("#resultadoBusqueda").html('<p>JQUERY VACIO</p>');
});

function buscar() {
    var textoBusqueda = $("input#busqueda").val();
 
     if (textoBusqueda != "") {
        $.post("buscar.php", {valorBusqueda: textoBusqueda}, function(mensaje) {
            $("#resultadoBusqueda").html(mensaje);
         }); 
     } else { 
        $("#resultadoBusqueda").html('<p>JQUERY VACIO</p>');
        };
};
</script>

Opcionalmente podríamos usar los métodos de callback de jQuery, como .done.fail, o incluso console.log, dentro del $.post para informar al cliente del estado de la petición.

No utilizamos el evento .submit porque lo que queremos es que muestre los resultados al escribir, sin tener que pulsar enter ni ningún botón.

En su lugar, para que el script obtenga los datos al escribir, debemos añadirle a nuestro input el evento onKeyUp de Javascript, el cual ejecutará la función buscar.

<input type="text" name="busqueda" id="busqueda" value="" placeholder="" maxlength="30" autocomplete="off" onKeyUp="buscar();" />
3 de 5: MySQL

En este ejemplo tengo una base de datos llamada mmv con cotejamiento utf8_spanish_ci. Dentro tengo una tabla llamada mmv001 que tiene cuatro columnas con el mismo cotejamiento. El motor de almacenamiento es InnoDB.

  1. id -> INT(5) -> UNIQUE -> A_I
  2. nombre -> VARCHAR(30)
  3. apellido -> VARCHAR(30)
  4. edad -> INT(2)

Es conveniente que el máximo de caracteres en el input del formulario sea de el mismo valor que una de las columnas (nombre o apellido en este caso) de la BBDD.

4 de 5: PHP

Ahora viene la parte interesante. Lo primero que debemos hacer es crear un archivo llamado buscar.php (o cualquiera que sea el nombre que hayamos puesto en la URL del $.post de jQuery).

El archivo debe estar totalmente vacío. Las etiquetas HTML (doctype, html, head, body, etc.) que puedan generar algunos editores de texto (como Dreamweaver) deben ser eliminadas.

Entonces le decimos al documento que vamos a escribir PHP y requerimos nuestro archivo de conexión a la BBDD.

<?php
//Archivo de conexión a la base de datos
require('conexion.php');
?>

Después es conveniente crear una variable que almacene la información que se enviará desde index.php con POST. Yo la llamaré consultaBusqueda.

<?php
//Archivo de conexión a la base de datos
require('conexion.php');

//Variable de búsqueda
$consultaBusqueda = $_POST['valorBusqueda'];
?>

Nota: valorBusqueda debe ser el nombre que hayamos puesto en jQuery. {valorBusqueda: textoBusqueda}

A continuación realizamos un filtro anti-XSS. Yo lo hago de la siguiente manera:

//Filtro anti-XSS
$caracteres_malos = array("<", ">", "\"", "'", "/", "<", ">", "'", "/");
$caracteres_buenos = array("& lt;", "& gt;", "& quot;", "& #x27;", "& #x2F;", "& #060;", "& #062;", "& #039;", "& #047;");
$consultaBusqueda = str_replace($caracteres_malos, $caracteres_buenos, $consultaBusqueda);

Nota: En $caracteres_buenos debes unir los ampersand (&) con los caracteres que vienen después, o no funcionará.

Una vez tenemos esto, creamos la variable que mostrará el contenido. La generamos vacía para evitar más adelante los E_NOTICE diciendo que no hemos establecido un valor para esa variable.

<?php
//Archivo de conexión a la base de datos
require('conexion.php');

//Variable de búsqueda
$consultaBusqueda = $_POST['valorBusqueda'];

//Filtro anti-XSS
$caracteres_malos = array("<", ">", "\"", "'", "/", "<", ">", "'", "/");
$caracteres_buenos = array("& lt;", "& gt;", "& quot;", "& #x27;", "& #x2F;", "& #060;", "& #062;", "& #039;", "& #047;");
$consultaBusqueda = str_replace($caracteres_malos, $caracteres_buenos, $consultaBusqueda);

//Variable vacía (para evitar los E_NOTICE)
$mensaje = "";
?>

Ahora es cuando comprobamos si $consultaBusqueda tiene algún parámetro. Si la variable está «seteada«, entonces procedemos a realizar la consulta a MySQL. Yo lo haré con estilo de procedimientos, pero no debería haber problema al hacerlo orientado a objetos.

<?php
//Archivo de conexión a la base de datos
require('conexion.php');

//Variable de búsqueda
$consultaBusqueda = $_POST['valorBusqueda'];

//Filtro anti-XSS
$caracteres_malos = array("<", ">", "\"", "'", "/", "<", ">", "'", "/");
$caracteres_buenos = array("& lt;", "& gt;", "& quot;", "& #x27;", "& #x2F;", "& #060;", "& #062;", "& #039;", "& #047;");
$consultaBusqueda = str_replace($caracteres_malos, $caracteres_buenos, $consultaBusqueda);

//Variable vacía (para evitar los E_NOTICE)
$mensaje = "";

//Comprueba si $consultaBusqueda está seteado
if (isset($consultaBusqueda)) {

	//Selecciona todo de la tabla mmv001 
	//donde el nombre sea igual a $consultaBusqueda, 
	//o el apellido sea igual a $consultaBusqueda, 
	//o $consultaBusqueda sea igual a nombre + (espacio) + apellido
	$consulta = mysqli_query($conexion, "SELECT * FROM mmv001
	WHERE nombre COLLATE UTF8_SPANISH_CI LIKE '%$consultaBusqueda%' 
	OR apellido COLLATE UTF8_SPANISH_CI LIKE '%$consultaBusqueda%'
	OR CONCAT(nombre,' ',apellido) COLLATE UTF8_SPANISH_CI LIKE '%$consultaBusqueda%'
	");

};
?>

Ahora vamos a obtener la cantidad de filas que devuelve la consulta para después hacer una condicional donde, si es igual a cero es porque no hay registros con esos datos (lo escrito en el input), y sino, muestra los datos en un bucle while. Por último, hacemos un echo de $mensaje y listo.

<?php
//Archivo de conexión a la base de datos
require('conexion.php');

//Variable de búsqueda
$consultaBusqueda = $_POST['valorBusqueda'];

//Filtro anti-XSS
$caracteres_malos = array("<", ">", "\"", "'", "/", "<", ">", "'", "/");
$caracteres_buenos = array("& lt;", "& gt;", "& quot;", "& #x27;", "& #x2F;", "& #060;", "& #062;", "& #039;", "& #047;");
$consultaBusqueda = str_replace($caracteres_malos, $caracteres_buenos, $consultaBusqueda);

//Variable vacía (para evitar los E_NOTICE)
$mensaje = "";


//Comprueba si $consultaBusqueda está seteado
if (isset($consultaBusqueda)) {

	//Selecciona todo de la tabla mmv001 
	//donde el nombre sea igual a $consultaBusqueda, 
	//o el apellido sea igual a $consultaBusqueda, 
	//o $consultaBusqueda sea igual a nombre + (espacio) + apellido
	$consulta = mysqli_query($conexion, "SELECT * FROM mmv001
	WHERE nombre COLLATE UTF8_SPANISH_CI LIKE '%$consultaBusqueda%' 
	OR apellido COLLATE UTF8_SPANISH_CI LIKE '%$consultaBusqueda%'
	OR CONCAT(nombre,' ',apellido) COLLATE UTF8_SPANISH_CI LIKE '%$consultaBusqueda%'
	");

	//Obtiene la cantidad de filas que hay en la consulta
	$filas = mysqli_num_rows($consulta);

	//Si no existe ninguna fila que sea igual a $consultaBusqueda, entonces mostramos el siguiente mensaje
	if ($filas === 0) {
		$mensaje = "<p>No hay ningún usuario con ese nombre y/o apellido</p>";
	} else {
		//Si existe alguna fila que sea igual a $consultaBusqueda, entonces mostramos el siguiente mensaje
		echo 'Resultados para <strong>'.$consultaBusqueda.'</strong>';

		//La variable $resultado contiene el array que se genera en la consulta, así que obtenemos los datos y los mostramos en un bucle
		while($resultados = mysqli_fetch_array($consulta)) {
			$nombre = $resultados['nombre'];
			$apellido = $resultados['apellido'];
			$edad = $resultados['edad'];

			//Output
			$mensaje .= '
			<p>
			<strong>Nombre:</strong> ' . $nombre . '<br>
			<strong>Apellido:</strong> ' . $apellido . '<br>
			<strong>Edad:</strong> ' . $edad . '<br>
			</p>';

		};//Fin while $resultados

	}; //Fin else $filas

};//Fin isset $consultaBusqueda

//Devolvemos el mensaje que tomará jQuery
echo $mensaje;
?>
5 de 5: Ejemplos

Una vez hecho todo lo anterior, podemos observar los resultados finales. Yo no lo he aplicado ningún estilo, pero se puede hacer sin problemas.

Donde el ID "resultadoBusqueda" está definido en jQuery como "JQUERY VACIO"

El ID «resultadoBusqueda» está definido en jQuery como «JQUERY VACIO»

Resultado para "pe", donde en la base de datos hay "Pérez" y "Pedro. Vemos que es case insensitive.

Resultado para «pe», donde en la base de datos hay «Pérez» y «Pedro», vemos que es case-insensitive

CONCAT(nombre,' ',apellido) COLLATE UTF8_SPANISH_CI LIKE '%$consultaBusqueda%'

Resultado de la consulta CONCAT(nombre,’ ‘,apellido) COLLATE UTF8_SPANISH_CI LIKE ‘%$consultaBusqueda%’

Output

Conclusión:

Es más fácil de lo que parece, es todo cuestión de comprender como funciona cada parte, desde el HTML hasta la base de datos. Si te sirvió, por favor, compártelo.

Espero que el tutorial haya sido de utilidad. Nos leemos, ¡saludos!

101 comentarios

  1. Excelente, si sigues tu tutorial no hay pierde. Hay gente que quiere ya todo hecho y el tema aquí es aprender, gracias

    Me gusta

  2. jose · diciembre 9, 2019

    justo lo que buscaba gracias por el tutorial

    Me gusta

  3. Mario · febrero 12, 2020

    Bárbaro! Funciona de maravillas. Muchas Gracias. Gran Aporte!!!

    Me gusta

  4. Juan Manuel Muñoz · abril 20, 2020

    Todos los comentarios te alaban por tu gran esfuerzo. Yo estoy de acuerdo. Pero como soy muy novato es esto, necesito la llamada a jQuery o ajax y cómo puedo obtener el resultado en una variable que no sea un mensaje en la pantalla, par usarlo con fines de validación (por ejemplo).
    Gracias anticipadas.
    Un saludo.

    Me gusta

Deja un comentario