jueves, 12 de febrero de 2009

Juntando todo: AJAX + XML + PHP + MySQL

En este ejemplo llegaremos finalmente a combinar todas las piezas que hemos estado revisando. Para poder dar inicio, es necesario que la tabla autores -creada en el post anterior- cuente con algunos registros, para lo que utilizamos la siguiente instrucción:
INSERT INTO `autores` ( `Id` , `Nombre` , `Nacionalidad` , `Profesion` )
VALUES (
'1', 'Juan Rulfo', 'Mexicano', 'Novelista'
),(
'2', 'Pablo Neruda', 'Chileno', 'Poeta'
),(
'3', 'Miguel de Cervantes Saavedra', 'Español', 'Novelista, poeta y dramaturgo'
),(
'4', 'Jorge Luis Borges', 'Argentino', 'Ensayista, escritor y poeta'
);

que al ejecutarla en nuestro analizador de consultas -ya sea phpMyAdmin, el cliente de la línea de comandos o algún administrador visual-, inserta estos cuatro registros en la tabla.

Bien, ahora el último paso para completar la actividad es acceder a nuestra base de datos obras_literarias con PHP al recibir una petición de AJAX. El siguiente esquema que ilustra un poco el la forma en que interactuarán los diferentes componentes para este propósito:


Programación del lado del cliente

El cliente Ajax de este ejemplo tomará como base el último post sobre la refactarización, agregando un formulario para capturar el nombre de un autor que se consultará en la base de datos. El código completo es el siguiente:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Sexto ejemplo con Ajax, combinándolo con PHP, XML y MySQL</title>
<script language="javascript">
var READY_STATE_COMPLETE=4;
var peticionHttp = null;
//Función que devuelve el objeto de la clase XMLHttpRequest
function inicializaXhr() {
    if(window.XMLHttpRequest) {
        return new XMLHttpRequest();
}
    else if(window.ActiveXObject) {
        return new ActiveXObject("Microsoft.XMLHTTP");
    }
}

function creaCadenaDeDatos() {
    var nombre = document.getElementById("txtNombre");
    return "txtNombre=" + encodeURIComponent(nombre.value) + "&nocache=" + Math.random();
}
//Función que inicia todo el proceso y lleva a cabo la petición al servidor
function consulta() {
peticionHttp = inicializaXhr();
if(peticionHttp) {
peticionHttp.onreadystatechange = procesaRespuestaXML;
peticionHttp.open("POST", "http://localhost/consultaMySQL.php", true);
peticionHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
var datos = creaCadenaDeDatos();
peticionHttp.send(datos);
}
}
//Función en la que se procesa el documento XML recibido
function procesaRespuestaXML() {
if(peticionHttp.readyState == READY_STATE_COMPLETE) {
if(peticionHttp.status == 200) {
var documentoXml = peticionHttp.responseXML; //Creamos el objeto de tipo documento XML
if(!documentoXml.getElementsByTagName('Error')[0]) {//Comprobamos que no se regrese un error como respuesta
var root = documentoXml.getElementsByTagName('Consulta')[0]; //Obtenemos la raíz del documento
//Accedermos al contenido de cada campo contenido en el elemento Informacion
var tabla = root.getElementsByTagName('Tabla')[0];
exploraRegistrosDeLaTabla(tabla);//Desplegamos todos los registros del elemento Tabla
}
else {//En caso de obtener un error desplegamos el mensaje de éste
var error = documentoXml.getElementsByTagName('Error')[0].firstChild.nodeValue;
alert(error);
}
}
}
}
/*Función que recibe el elemento que contiene los registros del documento XML, recorre cada uno de los
/*registros y almacena el valor de todos sus campos en el objeto objRenglon, al que después envía como
/*parámetro a la función muestraRenglon para desplegarlos en la tabla especificada
*/
function exploraRegistrosDeLaTabla(tablaXML) {
var objRenglon = new Object();
var tope1 = tablaXML.getElementsByTagName('Registro').length;
for(var i = 0; i < tope1; i++) {
registro = tablaXML.getElementsByTagName('Registro')[i];
tope2 = registro.getElementsByTagName('Campo').length;
for(var j = 0; j < tope2; j++) {
campo = registro.getElementsByTagName('Campo')[j];
objRenglon[campo.getAttribute('nombre')] = campo.firstChild.nodeValue;
}
muestraRenglon(objRenglon, 'tblInfo');
}
}
/**función que despliega un renglon dentro de la tabla HTML específicada a partir de las propiedades del
/* objeto objRenglon
*/
function muestraRenglon(objRenglon, idTabla) {
var tbody = document.getElementById(idTabla).getElementsByTagName("TBODY")[0]; //Referencia a la tabla
var renglon = document.createElement("TR"); //Crea un renglon
for(var llave in objRenglon){
var columna = document.createElement("TD"); //Crea una columna
columna.appendChild(document.createTextNode(objRenglon[llave])); //Agrega texto a la columna
renglon.appendChild(columna); //Agrega la columna al renglon
}
tbody.appendChild(renglon); //Agrega el renglon a la tabla
}
</script>
</head>
<body>
<form>
Nombre del autor : <input type="text" id="txtNombre" size="40" />
<input type="button" value="Buscar autor" onclick="consulta()" />
</form>
<table id="tblInfo" cellspacing="1">
<tbody>
<tr style="background:#E1E1E1">
<td><strong>ID</strong></td>
<td><strong>Nombre</strong></td>
<td><strong>Nacionalidad</strong></td>
<td><strong>Profesión</strong></td>
</tr>
</tbody>
</table>
</body>
</html>

En este ejemplo se tomará el nombre capturado en el cuadro de texto txtNombre, para enviarlo como un parámetro dentro de la petición para que PHP, por lo que también vemos que se incluye la función creaCadenaDeDatos():
function creaCadenaDeDatos() {
var nombre = document.getElementById("txtNombre");
return "txtNombre=" + encodeURIComponent(autor.value) "&nocache=" + Math.random();
}

para generar la cadena que se enviará en a través de del método send que aparece en la función consulta(), justo después de crear esta cadena:
peticionHttp.onreadystatechange = procesaRespuestaXML;
peticionHttp.open("POST", "http://localhost/consultaMySQL.php", true);
var cadenaDeDatos = creaCadenaDeDAtos();
peticionHttp.send(cadenaDeDatos);

Además, dentro de la función procesaResuestaXML se incluye una validación que comprueba si se generó algún error dentro del programa PHP al comprobar la existencia del elemento Error dentro del documento XML recibido:

var documentoXml = peticionHttp.responseXML;
if(!documentoXml.getElementsByTagName('Error')[0]) {
    var root = documentoXml.getElementsByTagName('Consulta')[0];
    var tabla = root.getElementsByTagName('Tabla')[0];
    exploraRegistrosDeLaTabla(tabla);
}
else {
    var error = documentoXml.getElementsByTagName('Error')[0].firstChild.nodeValue;
    alert(error);
}


de no existir la respuesta se procesa normalmente, en caso contrario se obtiene el contenido del elemento Error y lo despliega con un alert.

Programación del lado del servidor

¿Qué es lo que se hará en PHP?, bueno pués como dijimos accederá a la base de datos para ejecutar una consulta con los datos recibidos, para esto nos conectaremos con el SMBD MySQL por medio del conjunto de funciones que proporciona la extensión mysql dentro de PHP. También existe otra extensión que permite realizar un acceso mejorado a bases de datos MySQL llamada mysqli, la cual veremos en ejemplos posteriores ya que para éste bastará con la extensión básica.

Nuestro programa PHP recibirá el nombre de consultaMySQL.php y el código fuente completo que contendrá es el siguiente:


<?
require("ConsultaXML.php"); //Referencia al archivo que contiene la clase ConsultaXML
//Creación del documento XML
$consultaXML = new ConsultaXML();
//Parámetros para la conexión a la base de datos
$servidor = "localhost";
$usuario = "root";
$contrasenia = "[TuConstraseña]";
$baseDeDatos = "obras_literarias";
$conexion = mysql_connect($servidor,$usuario,$contrasenia); //Abrimos la conexión con MySQL
if(mysql_select_db($baseDeDatos,$conexion)) { //Accedemos a la base de datos
//Establecemos que la información que se intermabiará estará codificada en utf-8
mysql_query("SET NAMES 'utf8'",$conexion);
//Comprobamos que los datos se enviaron correctamente
if(isset($_POST['txtNombre']) && ($nombre = stripslashes(trim($_POST['txtNombre'])))!="") {
$consulta = "SELECT * FROM autores WHERE Nombre LIKE '%".addslashes($nombre)."%'";
$resultado = mysql_query($consulta,$conexion); //Realizamos la consulta
if($resultado) {
if(mysql_num_rows($resultado) > 0){//Comprobamos que si encontraron registros
//Creamos el documento XML con el resultado de la consulta
$consultaXML->iniciaDocumento();
$consultaXML->startElement("Tabla");
$consultaXML->writeAttribute("nombre","autores");
while($renglon = mysql_fetch_assoc($resultado)) {//Extrae un renglon del resultado como un arreglo asosciativo
$consultaXML->creaRegistroDesdeArreglo($renglon, "Id");
}
$consultaXML->endElement();
$consultaXML->salida();
}
else{
$mensajeDeError = "Autor no encontrado";
}
}
else {
$mensajeDeError = mysql_error();
}
}
else {
$mensajeDeError = "Datos imcompletos";
}
}
else{
$mensajeDeError = mysql_error();
}
if($mensajeDeError != "") {//En caso de que exista algún mensaje de error lo devolvemos como respuesta
$consultaXML->iniciaDocumento("Error");
$consultaXML->text($mensajeDeError);
$consultaXML->salida();
}
?>

Dentro de él se utiliza nuevamente la clase ConsultaXML para generar la respuesta XML. Después de esto aparecen declaradas una serie de variables, las cuales servirán para configurar el acceso a la base de datos de MySQL:
$servidor = "localhost";
$usuario = "root";
$contrasenia = "[TuConstraseña]";
$baseDeDatos = "obras_literarias";

En la variable $constrasenia es donde deben escribir la constraseña de root que definieron al instalar el servidor de bases de datos MySQL, en caso de no haber definido una contraseña la variable debe contener una cadena vacía.

El código siguiente abre la conexión con el servidor de bases de datos con la función mysql_connect y accede a la base de datos obras_literarias con mysql_select_db.

Después de acceder a la base de datos se comprueba que se haya recibido el dato enviado por POST txtNombre, para después elaborar la consulta SQL con este dato:
$consulta = "SELECT * FROM autores WHERE Nombre LIKE '%".addslashes($nombre)."%'";
$resultado = mysql_query($consulta,$conexion);

La cual es ejecutada por medio de la función mysql_query.

El resultado devuelto, que guarda la referencia a la tabla generada por la consulta que es almacenada en la memoria del servidor de bases de datos, se utiliza para extraer cada uno de los registros de esta tabla, dentro del ciclo while que aparece.

Cada uno de los registros devueltos serán agregados al documento XML a través del método creaRegistroDesdeArreglo del objeto consultaXML.

Para probar el funcionamiento de todo el sistema, bastará con que almacenemos el archivo consultaXML.php en el directorio de páginas de nuestro servidor Web, y que abramos el archivo HTML que contiene el cliente AJAX en nuestro navegador.

Para probar un consulta, podemos escribir cualquiera de los nombres de los autores que en un principio dimos de alta:



Si introducimos algún nombre que no exista en la base de datos se desplegará el siguiente mensaje de error:



Conclusiones

Llegamos finalmente a la etapa de acceder a una base de datos, y ya hemos completado nuestro primer paso al realizar consultas sencillas a una tabla, con lo cual ya nos podemos hacer una idea de las posibilidades que tienen los sistemas basados en la tecnología AJAX, combinándose con programación del lado del servidor. Un uso común es el realizar validaciones en las bases de datos de los datos introducidos en nuestros formularios HTML, por ejemplo: utilizar este método para comprobar la existencia de un nombre de usuario dentro de los sistemas de cuentas de usuarios por Internet, donde vemos que el sistema nos alerta de sobre una existencia similar a la que acabamos de escribir, otra aplicación diferente sería utilizarlo para consultar y actualizar la información de un foro de discusión o de una página de publicación de noticias, también existe la posibilidad de trabajar con multimedia, y mucho más, por lo que el límite realmente es el que nosotros queramos poner. En los siguientes ejemplos veremos más operaciones con bases de datos en MySQL y el acceso a un recurso diferente: un servicio Web.

Hasta la próxima.

0 comentarios:

Publicar un comentario