miércoles, 25 de febrero de 2009

Otra forma de acceder a la base de datos con PHP


En un de los ejemplos anteriores creamos una clase en PHP para generar documentos XML que contuvieran los resultados de las consultas a una base de datos, en esta ocasión, crearé otra clase en PHP pero que, -como dice el título de este post- se encargará de manejar el acceso a la base de datos, ¿por qué? primero que nada recordemos que en las escuela nos hablaron de la importancia de separar nuestros proyectos de programación con bases de datos en tres capas: presentación, negocio y acceso a datos, y no solo porque de esta forma se ven más elaborados sino por el hecho de que así ya partimos de un muy buen principio de diseño, que ayudará a lograr un mejor proyecto de desarrollo. Por otro lado encontramos que al trabajar con capas nos ahorramos mucho esfuerzo de teclear código, además de que se facilita el mantenimiento de nuestros programas.


El gran beneficio: La principal ventaja que encuentro al utilizar capas en nuestro desarrollo es que al producirse cambios en los requerimientos de nuestros sistemas, realizar modificaciones al código resulta mucho más sencillo. Por ejemplo, si tenemos cambiar de sistema manejador de base de datos de MySQL a MS SQL Server, dentro de nuestro código solo tendremos que realizar cambios en la capa de acceso a bases de datos, dejando prácticamente intacto el resto del código, ya que veamos el ejemplo se entenderá bien como es esto.

Puntos previos a tener en cuenta

Como consideraciones previas, tendremos en cuenta que las responsabilidades que recaerán sobre esta clase son:
  • Conectarse a la base de datos.
  • Acceder a la base de datos.
  • Ejecutar consultas SQL.
  • Insertar registros.
  • Actualizar registros.
  • Borrar registros.
  • Desconectarse de la base de datos.
Al momento de traducir estos requerimientos a la programación, irán surgiendo una serie de campos y métodos que se utilizarán para realizar las tareas necesarias.

Así, las funciones mysql (mysql_connect, mysql_select_db, mysql_query, etc.) que antes se utilizaban dentro del código, serán encapsuladas en esta serie de métodos.

Nota: Al referirme a las clases y objetos utilizo términos que aprendí durante mi incursión en el lenguaje Java, por lo que surgen algunas diferencias con los que manejan los desarrolladores más asiduos de .Net, en el que se refieren a propiedades y atributos de las clases en lugar de campos como en Java, pero ya que el modelo de programación con objetos de PHP5 se apegó más a Java creo que esta terminología queda bastante bien :D

Construyamos nuestra nueva clase para acceso a datos

Siguiendo con el trabajo, ahora les presento la clase que idee para cumplir el objetivo de manejar el acceso a una base de datos -un poco larga la verdad :P-:


<?php
//Clase Acceso a Datos
class AccesoADatos{
//Campos de la clase
private $conexion;
private $ipServidor;
private $usuario;
private $contrasenia;
private $baseDeDatos;
private $resultados;
private $mensaje;
private $registrosLeidos;
//Constructor de la clase
function __construct($ipServidor, $usuario, $contrasenia, $baseDeDatos) {
$this->ipServidor = $ipServidor;
$this->usuario = $usuario;
$this->contrasenia = $contrasenia;
$this->baseDeDatos = $baseDeDatos;
$this->conexion = false;
$this->registrosLeidos = 0;
$this->resultados = array();
$this->mensaje = "";
}
//Destructor de la clase Acceso a Datos
function __destruct() {
$this->liberaResultado();
$this->cierraConexion();
}
//Abre la conexión con el servidor de base de datos. Devuelve falso si hubo error
public function abreConexion() {
$estadoDeConexion = true;
$this->conexion = @mysql_connect($this->ipServidor,$this->usuario,$this->contrasenia);
if(mysql_select_db($this->baseDeDatos,$this->conexion)) {
mysql_query("SET NAMES 'utf8'",$this->conexion);
$estadoDeConexion = true;
}
else {
$estadoDeConexion = false;
$this->estableceError("No fue posible acceder a la base de datos, consulte con el administrador de la base de datos para solucionarlo.");
}
return $estadoDeConexion;
}
//Establece el mensaje de error e inicializa los contadores de resultados
private function estableceError($tipo) {
$this->registrosLeidos = 0;
$this->mensaje = "Error de acceso a datos: ".$tipo;
}
//Ejecuta una consulta SQL contenida en una cadena
public function consulta($consultaSQL, $idResultado = "") {
$estado = true;
if($this->conexion) {
$r = mysql_query($consultaSQL,$this->conexion);
if(mysql_errno() > 0) {
$this->estableceError("No se completó la operación solicitada ".mysql_error());
$estado = false;
}
else
$this->registrosLeidos = mysql_num_rows($r);
if($idResultado == "")
$this->resultados[0] = $r;
else
$this->resultados[$idResultado] = $r;
}
else {
$this->estableceError("Conexión con la base de datos no establecida.");
$estado = false;
}
return $estado;
}
//Borra él o los registros de una tabla que cumplan los parámetros recibidos
public function borraRegistro($tabla, $condicion = "1") {
$estado = true;
if($this->conexion) {
$comando = "DELETE FROM `$tabla` WHERE $condicion";
mysql_query($comando,$this->conexion);
if($this->numeroDeError = mysql_errno() > 0) {
$this->estableceError("No se completó la operación solicitada ".mysql_error());
$estado = false;
}
}
else {
$this->estableceError("Conexión con la base de datos no establecida.");
$estado = false;
}
return $estado;
}
//Inserta un registro en la tabla específicada utilizando como valores el arreglo de datos recibido
public function insertaRegistro($tabla, $datos) {
if($this->conexion) {
$comando = "INSERT INTO `$tabla` (";
foreach($datos as $llave=>$dato) {
$campos[]= "`$llave`";
$valores[] = $dato;
}
$comando.= implode(",",$campos).") VALUES('".implode("','",$valores)."')";
mysql_query($comando,$this->conexion);
if(mysql_errno($this->conexion) > 0)
$this->estableceError("No se completó la operación solicitada ".mysql_error());
return mysql_affected_rows($this->conexion);
}
else {
$this->estableceError("Conexión con la base de datos no establecida.");
return 0;
}
}
//Actualiza el registro de una tabla utilizando los párametors recibidos
public function actualizaRegistro($tabla, $nuevosDatos, $campoLlave) {
if($this->conexion) {
$comando = "UPDATE `$tabla` SET ";
foreach($nuevosDatos as $llave=>$dato)
$nuevosValores[] = "`$llave` = '$dato'";

foreach($campoLlave as $llave=>$dato)
$condicion[] = "`$llave` = '$dato'";
$comando.= implode(",",$nuevosValores)." WHERE ".implode(" AND ",$condicion);
mysql_query($comando,$this->conexion);
if(mysql_errno($this->conexion) > 0)
$this->estableceError("No se completó la operación solicitada ".mysql_error());
return mysql_affected_rows($this->conexion);
}
else {
$this->estableceError("Conexión con la base de datos no establecida.");
return 0;
}
}
//Regresa el contenido de un registro del resultado en un arreglo
public function devuelveArreglo($idResultado = "") {
if($idResultado=="")
$idResultado = 0;
if($this->resultados[$idResultado])
return mysql_fetch_assoc($this->resultados[$idResultado]);
else
return false;
}
//Devuelve el número de registros leidos por la última consulta
public function devuelveRegsLeidos() {
return $this->registrosLeidos;
}
//Devuelve el utimo mensaje de error generado
public function devuelveError() {
return $this->mensaje;
}
//Libera de la memoria del servidor el resultado de cualquier consulta realizada
public function liberaResultado() {
foreach($this->resultados as $llave=>$r) {
if($this->resultados[$llave])
mysql_free_result($this->resultados[$llave]);
}
$this->resultados = array();
}
//Cierra la conexión con el servidor de base de datos
public function cierraConexion() {
if($this->conexion){
mysql_close($this->conexion);
$this->conexion = false;
}
}
}
?>

La clase recibe el nombre de AccesoADatos, y dentro de esta clase aparece un primer método identificado como __construct, que es el método constructor de la clase, es llamado por PHP al momento de instanciarla. En caso de no declararlo, PHP llamaría a un constructor por defecto que poseen todas las clases, como en el caso de la clase ConsultaXML que programamos en uno de los posts anteriores.
function __construct($ipServidor, $usuario, $contrasenia, $baseDeDatos) {
$this->ipServidor = $ipServidor;
$this->usuario = $usuario;
$this->contrasenia = $contrasenia;
$this->baseDeDatos = $baseDeDatos;
$this->conexion = false;
$this->registrosLeidos = 0;
$this->resultados = array();
$this->mensaje = "";
}


Dentro del constructor se reciben los parámetros necesarios para realizar la conexión con el servidor de base de datos, y para acceder a la base de datos seleccionada. Además, se inicializan los campos que se utilizarán dentro de los métodos de la clase.

En el siguiente método encontramos el identificador __destruct:

function __destruct() {
$this->liberaResultado();
$this->cierraConexion();
}


El cual representa el destructor de la clase que es llamado automáticamente por PHP, al momento que todas las referencias a un objeto de la clase AccesoADatos sean removidas o cuando el objeto sea explícitamente destruido, o en cualquier orden en la finalización de la ejecución. Dentro de éste método se liberará la memoria de cualquier resultado de las consultas realizadas, para finalmente cerrar la conexión con el servidor de base de datos.

El método abreConexion() realiza la conexión con el servidor de base de datos MySQL, utilizando los parámetros $ipServidor, $usuario y $constrasenia, establecidos en el constructor de la clase.

mysql_connect($this->ipServidor,$this->usuario,$this->contrasenia);
if(mysql_select_db($this->baseDeDatos,$this->conexion)) {
mysql_query("SET NAMES 'utf8'",$this->conexion);
$estadoDeConexion = true;
}

Inmediatamente después accede a la base de datos especificada por el campo $baseDeDatos, y finalmente establece que el juego de caracteres que se utilizará en las consultas es UTF-8 a través de la consulta "SET NAMES utf8".

Todos los mensajes de error que se generen con las operaciones de acceso a la base de datos se establecerán con el método estableceError(), y estos mensajes podrán ser consultados desde fuera de la clase a través del método devuelveMensaje().

El método consulta(), recibe una cadena dentro del parámetro $consultaSQL que representa una consulta SQL para ejecutarla con la función mysql_query(),

$r = mysql_query($consultaSQL,$this->conexion);
if(mysql_errno() > 0) {
$this->estableceError("No se completó la operación solicitada ".mysql_error());
$estado = false;
}
else
$this->registrosLeidos = mysql_num_rows($r);
if($idResultado == "")
$this->resultados[0] = $r;
else
$this->resultados[$idResultado] = $r;


almacenando el resultado de la consulta dentro del arreglo $resultados, identificandola con el parámetro $idResultado que puede ser tanto una cadena como un valor numérico. En caso de que no se reciba un valor que identifique el resultado, este se almacena dentro del arreglo como el elemento 0 en el índice.

El método borraRegistro() eliminará uno o más registros en una tabla especificada, dependiendo de los parámetros que reciben, dentro de los que se incluye el nombre de la tabla y la condición del comando SQL.

El métodos insertaRegistro(), realiza la inserción de un registro en la tabla especificada utilizando los valores contenidos en el arreglo asociativo $datos, el cual debe poseer el formato 'llave'=>"valor", donde la llave representa el nombre del campo de la tabla y el valor es el dato que será insertado en este campo.

$comando = "INSERT INTO `$tabla` (";
foreach($datos as $llave=>$dato) {
$campos[]= "`$llave`";
$valores[] = $dato;
}
$comando = $comando.implode(",",$campos).") VALUES('".implode("','",$valores)."')";



Utilizando el ciclo foreach y la función implode para construir el comando INSERT que será ejecutado.

El método actualizaRegistro() es similar a insertaRegistro(), solo que además del nombre de la tabla y del arreglo de datos, se recibe segundo arreglo asociativo llamado $campoLlave, que se utiliza para identificar él o los registros que serán actualizados por este método.

El método devuelveArreglo(), nos permite acceder a cada uno de los renglones del resultado de una consulta identificado por $idResultado, almacenado este renglón dentro de un arreglo asociativo por medio de la función mysql_fetch_assoc.

Con este código crearemos el archivo AccesoADatos.php, que a partir de ahora utilizaremos en los siguientes ejemplos para acceder a nuestras bases de datos. Cómo una muestra del uso de esta nueva clase, refactorizaremos el código PHP del último ejemplo, es decir el archivo consultaMySQL.php, para generar un nuevo archivo PHP que posea con la siguiente contenido:


<?php
require("ConsultaXML.php"); //Referencia al archivo que contiene la clase ConsultaXML
require("AccesoADatos.php");//Referencia al archivo que contiene la clase AccesoADatos
$consultaXML = new ConsultaXML();//Creación del documento XML
//Parámetros para la conexión a la base de datos
$servidor = "localhost";
$usuario = "root";
$contrasenia = "[TuConstraseña]";
$baseDeDatos = "obras_literarias";
$acceso = new AccesoADatos($servidor,$usuario,$contrasenia,$baseDeDatos);
if($acceso->abreConexion()) { //Accedemos a la base de datos
//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)."%'";
if($acceso->consulta($consulta)) {//Realizamos la consulta y comprobamos el resultado
if($acceso->devuelveRegsLeidos() > 0){//Comprobamos si se encontraron registros
//Creamos el documento XML con el resultado de la consulta
$consultaXML->iniciaDocumento();
$consultaXML->startElement("Tabla");
$consultaXML->writeAttribute("nombre","autores");
while($renglon = $acceso->devuelveArreglo()) {//Extrae un renglon del resultado como un arreglo asosciativo
$consultaXML->creaRegistroDesdeArreglo($renglon, "Id");
}
$consultaXML->endElement();
$consultaXML->salida();
}
else{
$mensajeDeError = "Autor no encontrado";
}
}
else {
$mensajeDeError = $acceso->devuelveError();
}
}
else {
$mensajeDeError = "Datos imcompletos";
}
}
else {
$mensajeDeError = $acceso->devuelveError();
}
if($mensajeDeError != "") {//En caso de que exista algún mensaje de error lo devolvemos como respuesta
$consultaXML->iniciaDocumento("Error");
$consultaXML->text($mensajeDeError);
$consultaXML->salida();
}?>

El funcionamiento seguirá siendo exactamente el mismo, pero como vemos la cantidad de líneas de código y de variables se reduce, gracias al encapsulamiento de la clase. Y la parte más importa es, que nos abstraemos de la mayoría de los detalles en cuanto al acceso a la base de datos, por lo que en caso de cambiar de sistema manejador de base de datos, o agregar nuevas funcionalidades al acceso a datos, solo deberemos modificar esta clase.

En los siguientes ejemplos veremos como utilizar el resto de los métodos la clase AccesoADatos, programando una interface en Ajax y Javascript mucho más completa.

Hasta la próxima.

miércoles, 18 de febrero de 2009

Introducción al software libre y al open source


un montón de logotipos de diferentes sistemas libres

Este post trae incluida una disculpa ya que esta semana no podré dedicarle su tiempo a los temas que se habían venido tratando, pero como compensación publicaré un texto dedicado al software libre y al código abierto (open source) que realicé en algún momento del año pasado.

Ésta es una introducción acompañada con algunas notas históricas, solo que son de las que no contienen fechas, jajaja, ya que este post fue realizado sin una sesuda investigación previa, con el propósito de explicar de la manera más amena la importancia del software libre.

Sobre estos dos términos

Seguramente ya han escuchado hablar alguna ves de estos dos términos. Desafortunadamente en mi país que es México, la difusión en los medios clásicos de comunicación de este tema ha sido nula. Para comprender del todo lo que son, hay que entender la diferencia entre software propietario y software libre (o Free Software): el software propietario es aquel que se vende utilizando una licencia copyright (como las películas que compramos en un videoclub), cuando adquirimos un programa de esta clase, comprándolo en una tienda o por Internet, te haces con una copia del archivo o de los archivos ejecutables, es decir, los archivos que ejecuta tu PC para correr el programa, el detalle esta en que no tienes no tienes acceso al código fuente (es decir la lineas de texto en las que se describe el funcionamiento del programa), no puedes hacer copias de ese programa, mucho menos distribuirlo con tus amigos (legalmente no), y en algunos casos (como las licencias del software de Microsoft) te limitan el número de equipos en los que lo puedes instalar.

Ahora, en que se diferencia el software libre, libre(Free) no implica que es software gratuito necesariamente (eso es freeware), sino que hace referencia a la libertad que tiene la persona que lo adquiera para: usarlo, estudiarlo (ya que se incluye el código fuente), modificarlo y redistribuirlo libremente. La Software Free Foundation define cuatro libertades que cualquier usuario de software tiene, en las que el usuario tiene la libertad de: utilizar el programa, con cualquier propósito; de estudiar el funcionamiento del programa, a través del código fuente, y adaptarlo a sus necesidades; de distribuir copias, para ayudar a otros; de mejorar el programa y hacer públicas las mejoras, de modo que el resto de la comunidad se beneficie.

La base de la filosofía del software libre, es que el conocimiento debe ser compartido más no privativo, es decir, no hay problema si lucras con una idea, pero el código fuente debe estar a disposición del que necesite consultarlo. Si vendes un programa debes entregar a tu cliente el código fuente, y también si desarrollas un programa que donarás sin pago alguno, esto con el propósito de que el conocimiento no este en manos de un solo individuo o grupo, para crear un mercado libre en el que otros programadores puedan dar mantenimiento y hacer mejoras en los sistemas existentes, para que exista una libre competencia, en lugar de monopolios.

Ahora bien, ¿qué es Open Source?, este término quiere decir código fuente abierto, es decir que el código fuente de un programa Open Source esta disponible para cualquier persona que desee revisarlo. Esto implica que cualquier programa que sea Software Libre es Open Source, ya que para poder modificar un programa y hacer adaptaciones y/o mejoras es necesario contar con su código fuente, además de contar con los conocimientos necesarios de informática claro :P. La razón por la que surgió este segundo término es algunos programadores querian presentar el concepto de software libre a los grupos empresariales, sin que sonara tan radical, ya que Free Software que también puede entenderse como software gratis, cosa que no causaría mucho gracia entre el sector empresarial que deseara invertir en estos proyectos.

La filosofía que rige a los programadores de este software es diametralmente opuesta a la de empresas como Microsoft, de hecho fue Bill Gates el que invento el concepto de software propietario, ya que en un principio el software era libre, hasta que Bill Gates tuvo la genial idea de venderle el MS-DOS a IBM para sus equipos, pero sin darle los derechos sobre producto en sí, solo el derecho de distrubuirlo, vean la película los piratas de Silicon Valley para más información. Con lo que nunca contó Microsoft es que pudiera surgir una comunidad de programadores alrededor del mundo que colaboran entre sí, para desarrollar programas que compartieran entre ellos y con otros programadores de forma totalmente libre, sin fines de lucro, sino por su pasión por la programación, para lograr mejores programas de software. ¿Cómo competir contra un montón de programadores regados por el mundo?

Cuando aparecen el Ñu y el Pingüinito

El peor miedo de Microsoft debió ser el surgimiento de un sistema operativo totalmente libre, que compitiera con su Windows y eso se volvió realidad con la aparición de GNU/Linux. Este sistema operativo está disponible para cualquier humano sobre la fas de la tierra, que tenga acceso a una PC y a Internet. La gran mayoría de sus distribuciones (Fedora, OpenSuse, Mandriva, Ubuntu, entre otras) se pueden adquirir gratuitamente y en el caso de las distribuciones comerciales (como Red Hat), lo que realmente te venden es el servicio de asistencia y no el producto, , ya que en todo momento tu cuentas con el código fuente y tienes total libertad de revisarlo y modificarlo.

Otro dato interesante es que los programadores de software libre que fueron contratados por empresas privadas siguen trabajando para el software libre, como es el caso del mexicano Miguel de Icaza.

GNU/Linux surgió a través de la unión del nucleo del S.O. o kernel Linux con el conjunto de programas GNU.

Richard Stallman es uno de estos programadores, él es además la persona que ideo la filosofía que sustenta al software libre y que ha estado trabajando por años en el desarrollo de programas de software libre para el proyecto GNU.

Linus Torvalds es otro de los personajes importantes en el software libre, el desarrollo el kernel de Linux, que de hecho eso es realmente Linux, el núcleo del sistema, de ahí prescisamente que el nombre verdadero del S.O. es GNU/Linux y no Linux como muchos lo nombran.

GNU/Linux se basa en uno de los principios que rigen a la Ciencia que es que el conocimiento debe ser libre, es quizás una de las razones por las que es en las universidades donde se han desarrollado varias de las diferentes distribuciones. Aunque también existen empresas comerciales como Sun Mycrosistems o IBM que apoyan el desarrollo del software libre, a través de proyectos como OpenOffice, Eclipse o la plataforma Java. El mayor buscador del mundo en Internet Google funciona con software libre.

También muchos gobiernos en distintas naciones han comenzado a migrar sus sistemas hacia el software libre y es GNU/Linux uno de los principales pioneros. Por ejemplo en Extremadura España desarrollaron su propia distribución de GNU/Linux para casa y escuela llamada GNULinex, en México estudiantes de la UNAM ya desarrollaron su propia distribución llamada Jarro Negro, los Argentinos también hicieron la suya, el gobierno de Inglaterra fundó en 2005 la Open Source Academy (Academia de Fuente Abierta), destinado a fomentar el uso de ese tipo de sistemas abiertos en el sector público. China ya empezó a migrar todos los sistemas operativos del gobierno a GNU/Linux, y hay otros gobiernos alrededor del mundo que piensan hacer lo mismo, entre otras razones porque cuentan con el código fuente y eso les da la mayor seguridad.

Al principio Microsoft decidió solo ignorar la presencia de estos sistemas, pero actualmente esta gastando una gran cantidad de recursos para contra atacarlos. Una de las armas que ha utilizado es el desprestigio a través de distintos medios de comunicación, el propio Bill Gates llegó a llamar comunistas a los programadores de software libre, además han lanzado críticas a que gobiernos como el de China estén utilizando GNU/Linux. Cómo esto no funcionó, decidieron "liberar parte de su código fuente", pero con varias restricciones que no cumplen con las 4 libertades de la OSF. Actualmente la principal arma con la que Microsoft cuenta es la plataforma .Net, en la que de hecho han invertido la mayor cantidad de sus recursos. Pero la comunidad de software no se quedó pasiva y contra ataco con el proyecto Mono, dirigido por Miguel de Icaza, que permite ejecutar las aplicaciones .Net también en Linux. Y así la batalla continua de estos sistemas operativos continúa hasta la actualidad...

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.

martes, 10 de febrero de 2009

Introducción al uso de MySQL



¡Saludos nuevamente blogueros!

En este principio de semana nos internaremos en una de las partes que considero más interesantes sobre estos menesteres de Ajax: el acceso a las bases de datos. Aquí hay que hacer una anotación muy importante y es que la utilización de bases de datos dependerá de la programación del lado del servidor, que para los ejemplos siguientes continuaremos seguirá siendo PHP, pero siempre recordando que PHP no es el único lenguaje que podría utilizarse para programar este servicio, por ejemplo también podríamos utilizar un servicio Web en .Net. En fin, entonces hay que conocer la capacidad de PHP para conectarse a diferentes sistemas manejadores de bases de datos, y es aquí donde podemos ver que este lenguaje brinda una gran cantidad de opciones:
* Adabas D
* dBase
* Empress
* FilePro (read-only)
* Hyperwave
* IBM DB2
* Informix
* Ingres
* InterBase
* FrontBase
* mSQL
* Direct MS-SQL
* MySQL
* ODBC
* Oracle (OCI7 and OCI8)
* Ovrimos
* PostgreSQL
* Solid
* Sybase
* Velocis
* Unix dbm

También soporta la extensión DBX de abstracción de base de datos y ODBC (el Estándar Abierto de Conexión con Bases de Datos), la cuales le permiten conectarse forma transparente a cualquier base de datos que soporte estos estándares.

Como podrán imaginar por el título la opción que estoy considerando como SMBD es MySQL, jeje, debido a las experiencias que ya he tenido con su uso y a las diferentes ventajas que nos ofrece, entre ellas destacan:
  • Ser un sistema libre, multiusuario y multiplataforma.
  • Utilizar bases de datos relacionales.
  • Ser un sistema robusto para manejar gran cantidad de información.
  • Acceso rápido a los datos.
  • Contar con una implementación del lenguaje SQL muy completa.
  • Ofrece seguridad.
  • Servicios de replicación, transacciones, llaves foráneas y otros más que en nuestra clases de bases de datos vimos que eran muy importante :P.

Consideraciones previas


En versiones de PHP anteriores a la 5 el módulo para manejo de MySQL se encontraba dentro de PHP de manera nativa, es decir, venía como parte del núcleo del CGI de PHP, sin embargo en las versiones que siguieron a partir de ésta se encuentra como una extensión más que puede o no ser habilitada para utilizarse, por lo que hay que estar seguro que contamos con esta extensión dentro de PHP antes de continuar.

También hay que hacer la distinción entre SQL y MySQL, el primero es un lenguaje estándar para el acceso a bases de datos basado en consultas, el segundo es un sistema manejador de bases de datos que implementa el lenguaje SQL, para permitirnos acceder a la información contenida en las bases de datos que gestiona. Es común que en la escuela nos enseñen el lenguaje SQL de manera teórica antes de pasar a la práctica con sistemas como MySQL, SQL Server de Microsoft, Oracle, PostgreSQL, etc., que son los sistemas donde se utiliza toda la teoría de base de datos que estudiamos.

En éste punto partiremos del hecho de que ya conocemos el lenguaje SQL al menos en sus instrucciones básicas (SELECT, INSERT, UPDATE, DELETE, etc.), sin embargo es bueno contar con una guía a la mano sobre el lenguaje, las cuales pueden encontrar aquí:

Manual de SQL
Tutorial de SQL


Instalación de MySQL

Existen varios formas de instalar MySQL, todo depende del sistema operativo que estemos utilizando, y en la página oficial de MySQL podemos encontrar la siguiente sección completamente en español: Instalar MySQL, en la cual nos guían en los pasos que hay que seguir para llevar a cabo la instalación de este servidor en cualquier sistema operativo, ya sea Windows, GNU/Linux, Unix o MacOS.

La otra alternativa, que yo recomiendo más si se trata de introducirnos al manejo de MySQL, es utilizar un paquete de instalación que además de este servidor ya cuente con el servidor Apache, PHP y el sistema phpMyAdmin, debido a que éste último sistema -que depende de los dos anteriores- nos facilita enormemente el trabajo con las bases de datos. El instalador que podemos utilizar en Windows para este propósito es el AppServ, que ya habíamos mencionado en post anteriores.

Existe otro paquete que nos instala Apache, PHP y MySQL que es el XAMPP, que incluye n interprete para Perl. Adicionalmente puede incluir también al phpMyAdmin. La ventaja que XAMPP tiene sobre el AppServ es que lo encontramos en diferentes sistemas operativos como son Microsoft Windows, GNU/Linux, Solaris y MacOS X.

Si ya contamos con el servidor Apache con PHP, podemos simplemente instalar el servidor MySQL como por nuestra cuenta, como nos muestra la guía que aparece en la página oficial de MySQL que pusimos más arriba, por ejemplo para llevar a cabo la instalación automática en Windows encontramos la sección de descargas MySQL Dowloads, donde podemos elegir entre el servidor MySQL comunitario -gratuito- y el servidor MySQL empresarial -de paga-. El phpMyAmin también podemos por cuenta propia descargandolo desde su página oficial http://www.phpmyadmin.net/home_page/.

En la mayoría de las instalaciones, incluidas las de AppServ y XAMPP, deberemos establecer una contraseña para el usuario root, ya que como recordarán MySQL es un sistema multiusuario , y maneja una tabla propia de los usuarios que pueden acceder a sus bases de datos, y el usuario que tiene todos los privilegios de acceso a las bases de datos es precisamente root, por lo que si definimos una contraseña deberemos procurar recordarla, ya que para funciones de administración de la base de datos utilizaremos esta cuenta en varias ocasiones .


Manos a la obra

Suponiendo que ya hemos instalado MySQL, el paso que sigue es comenzar a utilizarlo, para este propósito utilizaré ejemplos tanto con phpMyAdmin como con instrucciones SQL. El phpMyAdmin es un sistema bastante intuitivo pero aún así incluiré explicaciones de cada paso que se vaya siguiendo.

Como menciona el contenido de su liga, phpMyAdmin es una aplicación Web programada en PHP, por lo que accederemos a él por medio de nuestro navegador Web, un ejemplo sería el siguiente:
http://localhost/phpMyAdmin/

Para acceder por medio del símbolo del sistema de Windows podemos utilizar el MySQL Command Line Client (Cliente de Línea de Comandos de MySQL), que es una aplicación que encontramos con todas las instalaciones mencionadas. Si en la instalación definimos una contraseña para root, al inicio este programa nos la solicitará.

Después de autenticarnos como root, en phpMyAdmin podremos acceder a la siguiente pantalla:



Dentro de esta pantalla podemos ver las características del servidor de base de datos, las del phpMyAdmin, y una serie de opciones para empezar a trabajar. Por ejemplo encontramos la opción Bases de datos, que nos despliega las bases de datos con que contamos en el sistema:



El equivalente a esta opción en el Command Line Client sería:
mysql>show databases;

La opción que nos interesa es la que nos permite crear una nueva base de datos, en ese caso la llamaremos obras_literarias y dejaremos el cotejamiento por default:



Con lo cual se crea la base de datos obras_literarias, e inmediatamente podemos comenzar a utilizarla. Esta acción equivale a las siguientes instrucciones en la línea de comandos:
mysql>CREATE DATABASE `obras_literarias` ;
mysql>USE obras_literarias

De hecho, como podemos darnos cuenta phpMyAdmin nos muestra cada uno de los comandos que va ejecutando al momento de completar una determinada tarea, así que en realidad lo que hace este sistema, es ahorrarnos el trabajo de teclear muchas de las instrucciones que se tendrían que realizar desde la línea de comandos, además de que nos ofrece una interfaz mucho más amigable.

Ahora, el paso que sigue es crear una tabla dentro de la base de datos obras_literarias, trabajo que podemos hacer en el phpMyAdmin por medio de la opción Crear nueva tabla:



O, para hacerlo aún más fácil, simplemente hay que copiar el siguiente script:
CREATE TABLE `autores` (
`Id` MEDIUMINT NOT NULL ,
`Nombre` VARCHAR( 60 ) NOT NULL ,
`Nacionalidad` VARCHAR( 50 ) NOT NULL ,
`Profesion` VARCHAR( 60 ) NOT NULL ,
PRIMARY KEY ( `Id` )
) ENGINE = MYISAM ;

seleccionar la pestaña SQL, pegar el código ahí y ejecutarlo:



Con lo cual se creará la tabla autores, que contendrá los campos Id, Nombre, Nacionalidad y Profesion, que serán del tipo de dato y longitud especificados por el comando anterior.

El código anterior también lo podemos usar en la línea de comandos para obtener el mismo resultado.

De esta forma sin muchas complicaciones se pueden crear bases de datos y tablas utilizando cualquiera de las dos maneras. Se pueden seguir probando todas las opciones que nos ofrece este sistema y crear más tablas, pero en el ejemplo siguiente partiremos de el hecho de que la base de datos y la tabla que mostramos ya fueron creadas. El paso que ahora sigue es comenzar a utilizar esta base de datos con la programación de aplicaciones en PHP y Ajax, pero eso lo veremos en el próximo post.

Hasta luego.

viernes, 6 de febrero de 2009

Refactorizar el código que procesa XML, Parte 2

Refactorización del lado del cliente

Ahora como prometimos, atenderemos la cuestión del cliente. Las modificaciones se centraran en el modo de extraer y desplegar la información del documento XML. En el último ejemplo de Ajax a partir de la raíz de este documento, se extrajeron secuencialmente los valores de cada uno de los elementos que serían desplegados, almacenándolo en una serie de variables, que después eran pasadas a una función que las desplegaba agregándolas a los renglones de una tabla HTML:


function procesaRespuestaXML() {
if(peticionHttp.readyState == READY_STATE_COMPLETE) {
if(peticionHttp.status == 200) {
var documentoXml = peticionHttp.responseXML; //Creamos el objeto de tipo documento XML
var root = documentoXml.getElementsByTagName("Informacion")[0]; //Obtenemos la raíz del documento
//Accedermos al contenido de cada campo contenido en el elemento Informacion
var infoCliente = root.getElementsByTagName("Cliente")[0].firstChild.nodeValue;
muestraRenglon("Navegador",infoCliente);
var infoSoftware = root.getElementsByTagName("Software")[0].firstChild.nodeValue;
muestraRenglon("Software del servidor",infoSoftware);
var infoFecha = root.getElementsByTagName("Fecha")[0].firstChild.nodeValue;
muestraRenglon("Fecha del servidor",infoFecha);
var infoHora = root.getElementsByTagName("Hora")[0].firstChild.nodeValue;
muestraRenglon("Hora del servidor",infoHora);
}
}
}
//Función que agrega un nuevo renglon a una tabla especificada, utilizando los valores recibidos

function muestraRenglon(colEtiqueta, colValor){
var tbody = document.getElementById('tblInfo').getElementsByTagName("TBODY")[0]; //Referencia a la tabla
var renglon = document.createElement("TR"); //Crea un renglon
var columna = document.createElement("TD"); //Crea una columna
columna.appendChild(document.createTextNode(colEtiqueta)); //Agrega texto a la columna
renglon.appendChild(columna); //Agrega la columna al renglon
columna = document.createElement("TD"); //Crea una nueva columna
columna.appendChild(document.createTextNode(colValor));
renglon.appendChild(columna);
tbody.appendChild(renglon); //Agrega el renglon a la tabla
}


Éste método resultaría en un código bastante largo si aumentara el número de elementos que serán desplegados, así que lo mejor es automatizar el proceso de extraer cada uno de los elementos del documento XML, procurando evitar los pasos secuenciales en que cae la forma anterior. Para este propósito dentro del programa Javascript se utilizará una función adicional, y además se modificara la función muestraRenglon para hacerla más compacta.

De modo que el código completo del cliente será 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>Quinto ejemplo con Ajax, combinándolo con PHP y XML (Refactorizado)</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");
}
}
//Función que inicia todo el proceso y lleva a cabo la petición al servidor
function solicita() {
peticionHttp = inicializaXhr();
if(peticionHttp) {
peticionHttp.onreadystatechange = procesaRespuestaXML;
peticionHttp.open("POST", "http://localhost/generaXMLConClase.php", true);
peticionHttp.send(null);
}
}
//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
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
}
}
}
/*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>
Para realizar la petición al servidor Web
<input type="button" value="presiona aqui" onclick="solicita()" />
<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 ésta ocasión vemos que se hicieron unos cambios en la función procesaRespuestaXML, ya no se procesa el contenido completo del documento XML:
function procesaRespuestaXML() {
   if(peticionHttp.readyState == READY_STATE_COMPLETE) {
      if(peticionHttp.status == 200) {
         var documentoXml = peticionHttp.responseXML;
         var root = documentoXml.getElementsByTagName('Consulta')[0];
         var tabla = root.getElementsByTagName('Tabla')[0];
         exploraRegistrosDeLaTabla(tabla);
      }
   }
}

En cambio, después de acceder al elemento raíz del documento -en este caso Consulta- se crea el objeto tabla a partir del elemento Tabla, y éste es pasado como un parámetro a la función exploraRegistrosDeLaTabla, dentro de la cual se lleva todo a cabo la extracción de cada uno de los elementos que serán desplegados:
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');
   }
}


Ésta función esta formada por dos bucles for, dentro del primero se recorreran todos los elementos Registro que contenga el elemento Tabla, en el segundo bucle se recorrerán todos los elementos Campo que contenga del registro que sea accedido en el ciclo externo, almacenando los valores de cada uno de los campos en las propiedades del objeto objRenglon. Al terminar de recorrer todos los campos del registro se llamará a la función muestraRenglon, pesándole este objeto para que despliegue dentro de la tabla tblInfo un renglon que contenga los valores recogidos:
function muestraRenglon(objRenglon, idTabla) {
   var tbody = document.getElementById(idTabla).getElementsByTagName("TBODY")[0];
   var renglon = document.createElement("TR");
   for(var llave in objRenglon){
      var columna = document.createElement("TD");
      columna.appendChild(document.createTextNode(objRenglon[llave]));
      renglon.appendChild(columna);
   }
   tbody.appendChild(renglon);
}

Ésta función muestraRenglon tiene el propósito de agregar un nuevo renglón a la tabla especificada, con los valores que recibe como parámetro, solo que estos valores van contenidos dentro de las propiedades del objeto objRenglon, las cuales pueden ser extraídas como si se tratara de un arreglo asociativo dentro del bucle for de la función.

De esta forma queda finalizada la refactorización del cliente Ajax para el procesamiento de documentos XML, generados en PHP. Lo que queda en este caso es probar este nuevo programa almacenando en cliente en un archivo HTML, y guardando los archivos PHP ConsultaXML.php y generaXMLConClase.php en el directorio de páginas de nuestro servidor Web. Al probar el programa Javascript en el navegador deberemos obtener un resultado como el siguiente:



Conclusiones

El código PHP y Ajax que obtuvimos de este ejercicio nos será de gran utilidad en los ejemplos siguientes, sobre todo al momento de incursionar en el manejo de las bases de datos, al facilitarnos en gran medida el proceso de devolver el resultado de una consulta a nuestro cliente, así como el procesamiento de la información que éste lleva a cabo. El propósito de ésta práctica era contar con mejores armas para encarar los nuevos retos que nos estamos planteando, y aunque ahora puedan representa más trabajo, al adentrarse más en las aplicaciones de Ajax se verá rendir fruto a estos esfuerzos.

Hasta la próxima.

miércoles, 4 de febrero de 2009

Refactorizar el código que procesa XML, Parte 1


Los tres pasos de la buena programación

En el post anterior utilizamos un método para generar código XML del lado de PHP bastante simple, que funciona bien para el ejemplo debido la cantidad de datos que estábamos utilizando, sin embargo, al incrementarse el tamaño del documento XML generado, utilizar solamente llamadas a la función echo resultaría en un código fuente bastante largo y difícil de mantener, además la creación de documentos XML desde PHP se llevará a cabo en más de un programa, por lo que una forma de agilizar este proceso consiste en crear bloques de código reutilizables, que nos ahorren una parte considerable del trabajo al teclear nuestros programas. Una forma de hacerlo es utilizando grupos de funciones, cada una correspondiendo a un paso específico del proceso, otra forma sería el uso de clases para manejar el proceso completo, lo cual implicaría adentrarnos un poco en programación orientada a objetos en este lenguaje. Para la generación del documento XML diseñaremos una sencilla clase en PHP, utilizando el modelo de objetos de su versión 5. De esta forma la escritura de los siguientes programas será más ágil y sobre todo más fácil de mantener.


Consideraciones previas

La principal cuestión antes de continuar es conocer que formato tendrán los documentos XML que crearemos para los siguientes ejemplos, debido a que el próximo paso será el manejo de las bases de datos, consideraremos que los elementos del documento XML representarán los registros y campos obtenidos al realizar consultas a las bases de datos relacionales. Sería algo como lo que sigue:

<?xml version="1.0" encoding="utf-8"?>
<Consulta>
<Tabla nombre="Autores">
<Registro id="1">
<Campo nombre="Nombre">Juan Rulfo</Campo>
<Campo nombre="Nacionalidad">Mexicano</Campo>
<Campo nombre="Profesion">Novelista</Campo>
</Registro>
<Registro id="2">
<Campo nombre="Nombre">Pablo Neruda</Campo>
<Campo nombre="Nacionalidad">Chileno</Campo>
<Campo nombre="Profesion">Poeta</Campo>
</Registro>
<Registro id="3">
<Campo nombre="Nombre">Miguel de Cervantes Saavedra</Campo>
<Campo nombre="Nacionalidad">Español</Campo>
<Campo nombre="Profesion">Novelista, Poeta y Dramaturgo</Campo>
</Registro>
</Tabla>
</Consulta>


Así, los elementos principales estarán representados por la etiqueta Tabla, y cada tabla tendrá un identificador que será único, representado en este caso por el atributo nombre, que representa el nombre de la tabla dentro de la base de datos. En el ejemplo existe únicamente la tabla Autores, dentro de la cual encontramos un grupo de registros, los cuales se identifican por el atributo id. Dentro de todo registro, aparecerá una serie de elementos llamados Campos, que serán identificados por el atributo nombre, y los valores de cada campo están contenidos en sus propias etiquetas.

Refactorización del lado del Servidor

De forma que el código en PHP necesario para crear este tipo de documentos será el siguiente:


<?php
//Clase creada para manejar documentos XML que tengan el formato de una consulta a una base de datos
class ConsultaXML extends XMLWriter{
//Función que inicializa el documento XML, establece el contenido de su encabezado y abre el elemento raíz
function iniciaDocumento($elementoRaiz = "Consulta", $version = "1.0", $juegoDeCarateres = "UTF-8") {
$this->openMemory();
$this->setIndent(true);
$this->setIndentString(" ");
$this->startDocument($version,$juegosDeCaracteres);
$this->startElement($elementoRaiz);
}
/*
* Método que crea el elemento Campo dentro del documento, recibiendo como parámetros el nombre del campo
* y el valor que contendrá.
*/
public function creaCampo($nombreDelCampo, $valor) {
$this->startElement("Campo");
$this->writeAttribute("nombre",$nombreDelCampo);
$this->text($valor);
$this->endElement();
}
/*
* Método que crea el elemento Registro dentro del documento, recibiendo como parámetros el arreglo asociativo
* que contendrá los nombres y valores de los campos que poseerá el registro, junto con el identificaor del
* registro que será almacenado en su atributo "id".
*/
public function creaRegistroDesdeArreglo($arreglo, $idRegistro) {
$this->startElement("Registro");
$this->writeAttribute("id",$idRegistro);
if(is_array($arreglo)){
foreach ($arreglo as $llave => $valor){
$this->creaCampo($llave, $valor);
}
}
$this->endElement();
}
/*
* Método que crea el elemento Tabla dentro del documento, recibiendo como parámetros una matriz de arreglos
* asociativos, el nombre de la tabla y el nombre del campo que identificará a los registros que contendrá la
* tabla. Dentro de un ciclo se llamará a al método creaRegistro para cada uno de los arreglos asociativos
* que contenga la raíz, de esta forma se creará un documento XML que contenga todos los registros de una
* tabla obtenida en una consulta.
*/
public function creaTablaDesdeMatriz($matriz, $nombreDeLaTabla, $campoIdRegistro) {
$this->startElement("Tabla");
$this->writeAttribute("nombre",$nombreDeLaTabla);
if(is_array($matriz) && gettype($campoIdRegistro)=="string"){
foreach ($matriz as $llave => $renglon){
if($campoIdRegistro == "")
$this->creaRegistroDesdeArreglo($renglon, $llave);
else
$this->creaRegistroDesdeArreglo($renglon, $renglon[$campoIdRegistro]);
}
}
$this->endElement();
}

//Cierra el elemento raíz y finaliza el documento, regresando además el contenido del documento actual.
public function finalizaDocumento(){
$this->endElement();
$this->endDocument();
return $this->outputMemory();
}

/**
* Establece que el programa PHP actual generará como salida un archivo XML y envía a esta la salida el contenido del
* documento creado.
*/
public function salida(){
header('Content-type: text/xml');
echo $this->finalizaDocumento();
}
}
?>


Así, nuestra clase recibirá el nombre de ConsultaXML, ya que el propósito de ésta clase es crear documentos XML que representen conjuntos de datos estructurados, como lo que se obtienen de las consultas a bases de datos o a los archivos estructurados, o cualquier otro tipo de información que necesite de un formato similar.

Esta clase hereda sus métodos y propiedades de la clase predefinida XMLWriter, lo cual está indicado por la sentencia class ConsultaXML extends XMLWritter. Utilizamos esta clase predefinida porque ya posee todos los métodos necesarios para escribir documentos XML con etiquetas y atributos, adicionando además un control de errores para prevenir que los documentos creados contengan errores en su estructura. Otro punto importante es que XMLWriter permite dirigir el salida completa del archivo tanto a la memoria como al disco duro, de esta forma podemos utilizar la función echo para dirigir esta salida como respuesta del servidor al navegador, tal y como lo hicimos en el último ejemplo al respecto.

El primer método que encontramos en la clase es iniciaDocumento:
$this->openMemory();
$this->setIndent(true);
$this->setIndentString(" ");
$this->startDocument($version,$juegosDeCaracteres);
$this->startElement($elementoRaiz);

el cual deberá ser el primer método que se llama para crear el documento XML, en éste se establece que el nuevo objeto XMLWrite tendrá una salida en forma de cadena en la memoria, a través de la sentencia $this->openMemory(), se establece que existirá indentación hacia la derecha en el documento con $this->setIndent(true) y con $this->setIndentString(" ") definimos que el caracter espacio en blanco será utilizada para esta indentación. Se establece además el encabezado del documento, y finalmente se crea el primer elemento del documento, que es indicado por el parámetro $elementoRaiz. Como también podemos apreciar, en los parámetros se definen valores por defecto para facilitarnos el uso de este método, ya que éstos serán los que requeriremos en la mayoría de los ejemplos, teniendo siempre la posibilidad de establecer nuevos valores.

Los métodos creaCampo, creaRegistro y creaTabla llevarán a cabo toda la tarea de llenar el contenido del documento XML, permitiendo cada una de ellas crear los respectivos elementos Campo, Registro y Tabla, como habíamos establecido en las consideraciones previas.

El método finalizaDocumento cerrará la etiqueta del primer elemento que fue creado con iniciaDocumento, además finaliza el documento y devuelve todo su contenido como respuesta.

$this->endElement();
$this->endDocument();
return $this->outputMemory();

Esta respuesta puede ser recibida de diferentes formas, ya sea siendo almacenada en una variable como una cadena de texto, o mandada a alguna función para crear un archivo, o puede ser enviada a la función echo para el propósito que ya se describió, lo cual es precisamente lo que se hace en el siguiente método:
public function salida(){
header('Content-type: text/xml');
echo $this->getDocument();
}

Que antes de mandar el contenido como salida del programa, establece que el tipo de salida será la de un archivo XML, para que el navegador tenga conocimiento y la trate como tal.

Este código puede ser incluido dentro del script donde se vaya a llevar a cabo la generación del documento XML, o puede ser almacenado en un archivo .php aparte que será llamado por medio de la función require, lo cual personalmente recomiendo para facilitar su reutilización en otros programas. Para este ejemplo el código se almacenará en una archivo llamado ConsultaXML.php.

Implementación del nuevo código

Ahora pasemos a lo bueno, que es utilizar esta clase, para lo que escribiremos un nuevo programa en PHP cuyo archivo recibirá el nombre de generaXMLConClase.php, y creará un documento a partir de una matriz, conformada por arreglos asociativos. El código es el siguiente:


<?
require("PruebasPHP/ConsultaXML.php"); //Referencia al archivo que contiene la clase ConsultaXML
//Arreglos que representan la información del documento XML
$registro1 = array('idAutor'=>1,'Nombre'=>"Juan Rulfo",'Nacionalidad'=>"Mexicano",'Profesion'=>"Novelista");
$registro2 = array('idAutor'=>2,'Nombre'=>"Pablo Neruda",'Nacionalidad'=>"Chileno",'Profesion'=>"Poeta");
$registro3 = array('idAutor'=>3,'Nombre'=>"Miguel de Cervantes Saavedra",'Nacionalidad'=>"Español",'Profesion'=>"Novelista, Poeta y Dramaturgo");
$tabla = array($registro1, $registro2, $registro3);
//Creación del documento XML
$consultaXML = new ConsultaXML();
$consultaXML->iniciaDocumento();
$consultaXML->creaTablaDesdeMatriz($tabla, "Autores", "idAutor");
$consultaXML->salida();
?>


Nota: Tal vez está de más señalarlo pero recuerda que la ruta que coloques en la función require indica la ubicación del archivo ConsultaXML.php, por lo que si lo almacenas en un directorio distinto al de generaXMLConClase.php deberás indicar ese cambio en el código anterior.

Después del require, creamos una serie de arreglos asociativos que representan el contenido del documento XML definido previamente. En seguida se crea una instancia de la clase ConsultaXML llamada $consultaXML, y como vemos, al utilizar este objeto el código se vuelve bastante pequeño, que es una de las cosas que me gustan de la utilización de objetos: el encapsulamiento que ocurre dentro de los métodos, con el cual se facilita enormemente el trabajo con las clases diseñadas.

Al utilizar el método salida() todo el contenido será regresado al navegador como un archivo XML, y podemos probarlo utilizando un cliente como Internet Explorer o Mozilla Firefox, y mandando llamar al archivo PHP escribiendo una URL como la que sigue en su barra de direcciones:
http://localhost/generaXMLConClase.php

La salida se verá directamente en la pantalla del navegador, como se muestra en la siguiente ejemplo capturado, en el que se utilizó Firefox:



De ésta forma queda lista la refactorización del código para generación de XML del lado del servidor. El siguiente paso será llevar a cabo un cambio más, pero ahora del lado del cliente, para mejorar la forma en la que se acceda a los elementos del documento XML recibido. Sin embargo, ese será el tema de nuestro siguiente post.

Hasta la próxima.

martes, 3 de febrero de 2009

Generando una respuesta XML desde PHP


Saludos blogueros, y una disculpa porque no tuve la oportunidad de postear este fin de semana largo, pero ya estamos de vuelta para continuar con la esta serie de anotaciones sobre Ajax, ahora combinándolo con programación del lado del servidor.

En el ejemplo del último post al respecto, desde PHP se generó una respuesta en forma de contenido HTML, la cual fué recibida por una función en Javascript que desplegó este contenido directamente en la pantalla. Como se habrán dado cuenta existe una limitación en este método, y es que la respuesta consiste únicamente en texto plano, con formato HTML sí, pero cuyo único uso práctico sería desplegarse en pantalla. En uno de los ejemplos anteriores, vimos que una forma de obtener una respuesta del servidor con información estructurada, es utilizando archivos XML, cuya información puede ser procesada por una función Javascript utilizando el DOM. Ahora, como también indicamos la tecnología PHP no solo se limita a devolver contenido HTML, sino que también puede enviar una respuesta en diferentes tipos de archivos, entre ellos XML. Así, este ejemplo consistirá en programar un cliente Ajax que solicitará el archivo de un programa PHP, y esperará una respuesta XML, mientras del lado del servidor el programa PHP creará un grupo de datos, y generará un salida con éstos en forma de un archivo XML.

Para este ejemplo dejaremos un momento las obras literarias, y realizaremos una petición de información relacionada con nuestro servidor Web, solicitando los datos del navegador desde el que se realiza la petición, la información del software que utiliza el servidor Web, así como la fecha y hora del sistema operativo de este mismo servidor. La petición será procesada en PHP, y dado que la respuesta consistirá en varios datos, estos serán devueltos en formato XML, en el cual se indicarán tanto el nombre del dato que se devuelve como el valor que tienen cada uno, considerando los siguientes datos:

-Información del navegador utilizado por el usuario.
-Software del servidor Web.
-Fecha y hora del servidor Web.

El siguiente esquema puede ayudar a ilustrar el proceso completo:



Así, por medio de Ajax el cliente realizará una petición HTTP como las que ya hemos visto, solicitando el recurso http://localhost/generaXML.php. Desde el archivo generaXML.php se obtendrán los datos del servidor que se consideraron y a partir de ellos creará un documento XML, cuyo contenido será generado por medio de la función echo, utilizando previamente la función header, para indicarle al navegador que le será devuelto un archivo XML como respuesta. Este contenido será recibido por medio de Ajax como la respuesta a la petición inicial, y por medio de una función Javascript será desplegado en pantalla dentro de una tabla.

Programación del cliente

En este ejemplo, el cliente solamente contendrá un botón para iniciar la petición de la información, y la información será desplegada en una tabla HTML, una vez que se haya recibido la respuesta del servidor. El código HTML que deberemos escribir en nuestro editor de texto 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>Cuarto ejemplo con Ajax, combinándolo con PHP y XML</title>
<script language="javascript">
<!--Sitio del código Javascript-->
</script>
</head>
<body>
Para realizar la petición al servidor Web
<input type="button" value="presiona aqui" onclick="solicita()" />
<table id="tblInfo" cellspacing="1" bordercolor="#CCCCCC">
<tr style="background:#E1E1E1"><td><strong>Dato devuelto</strong></td>
<td><strong>Valor</strong></td>
</tr>
</tbody>
</table>
</body>
</html>


Recordando que la codificación del archivo al momento de almacenarse debe ser UTF-8. En este ejemplo nuevamente colocaremos por separado el código Javascript del HTML, de forma que para probarlo solo será necesario unir ambos, colocando el siguiente código Javascript dentro del las etiquetas que se señalan:

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");
   }
}
//Función que inicia todo el proceso y lleva a cabo la petición al servidor
function solicita() {
   peticionHttp = inicializaXhr();
   if(peticionHttp) {
      peticionHttp.onreadystatechange = procesaRespuestaXML;
      peticionHttp.open("POST", "http://localhost/generaXML.php", true);
      peticionHttp.send(null);
   }
}
//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
         var root = documentoXml.getElementsByTagName("Informacion")[0]; //Obtenemos la raíz del documento
         //Accedermos al contenido de cada campo contenido en el elemento Informacion
         var infoCliente = root.getElementsByTagName("Cliente")[0].firstChild.nodeValue;
         muestraRenglon("Navegador",infoCliente);
         var infoSoftware = root.getElementsByTagName("Software")[0].firstChild.nodeValue;
         muestraRenglon("Software del servidor",infoSoftware);
         var infoFecha = root.getElementsByTagName("Fecha")[0].firstChild.nodeValue;
         muestraRenglon("Fecha del servidor",infoFecha);
         var infoHora = root.getElementsByTagName("Hora")[0].firstChild.nodeValue;
         muestraRenglon("Hora del servidor",infoHora);
      }
   }
}
//Función que agrega un nuevo renglon a una tabla especificada, utilizando los valores recibidos
function muestraRenglon(colEtiqueta, colValor){
   var tbody = document.getElementById('tblInfo').getElementsByTagName("TBODY")[0]; //Referencia a la tabla
   var renglon = document.createElement("TR"); //Crea un renglon
   var columna = document.createElement("TD"); //Crea una columna
   columna.appendChild(document.createTextNode(colEtiqueta)); //Agrega texto a la columna
   renglon.appendChild(columna); //Agrega la columna al renglon
   columna = document.createElement("TD"); //Crea una nueva columna
   columna.appendChild(document.createTextNode(colValor));
   renglon.appendChild(columna);
   tbody.appendChild(renglon); //Agrega el renglon a la tabla
}

Revisando el código nos damos cuenta que reutilizamos parte del código de los ejemplos anteriores para llevar a cabo una petición que se inicia con la función solicita():
function solicita() {
  peticionHttp = inicializaXhr();
  if(peticionHttp) {
    peticionHttp.onreadystatechange = procesaRespuestaXML;
    peticionHttp.open("POST", "http://localhost/generaXML.php", true);
    peticionHttp.send(null);
  }
}

Dentro de la función solicita se crea el objeto peticionHttp, y después de validar que se creo correctamente con la condición, indicamos que en cuanto se reciba la respuesta del servidor Web, se ejecute la función procesaRespuestaXML, después de lo cual realizamos la petición del recurso, en este caso identificado por la URL http://localhost/generaXML.php. Para éste ejemplo no se enviarán datos al servidor Web, lo cual indicamos pasando al método send un valor null.

En la función procesaRespuestaXML es donde se lleva a cabo el procesamiento del documento XML, que es enviado como respuesta por el servidor Web:
function procesaRespuestaXML() {
  if(peticionHttp.readyState == READY_STATE_COMPLETE) {
    if(peticionHttp.status == 200) {
      ...

Después de que validamos que se haya recido una respuesta completa, podremos pasar a procesar el documento XML.
var documentoXml = peticionHttp.responseXML;
var root = documentoXml.getElementsByTagName("Informacion")[0];

En esta dos líneas creamos el objeto documentoXML, que utilizamos para acceder al contenido del documento, y en seguida creamos el objeto root, con el que accedemos a la raíz del oducmento XML, en este caso el elemento Información. En seguida, accederemos a cada uno de los campos que encontramos dentro del elemento éste elemento, almacenando el contenido de cada campo en su respectiva variable.
var infoCliente = root.getElementsByTagName("Cliente")[0].firstChild.nodeValue;
muestraRenglon("Navegador",infoCliente);
En las dos líneas anteriores que se tomaron como referencia a este proceso, el contenido del elemento Cliente es alamacenado en la variable infoCliente, y desplegado en la página HTML dentro de la tabla tblInfo por medio de la función muestraRenglon.

La función muestraRenglon utiliza la funcionalidad que brinda el DOM del navegador, para crear objetos de tipo renglón y columna, permitiendo que se agreguen dinámicamente nuevos renglones y columnas a una tabla HTML, identificada por el id tblInfo.

De esta forma queda concluida la programación del lado del cliente para este ejemplo, pudiendo guardar este archivo HTML con cualquier nombre que nosotros deseemos, para probarlo posteriomente. Ahora, solo queda programar la aplicación PHP, que generará el documento XML de respuesta.

Programación del lado del servidor

Este pequeño programa recibirá el nombre de generaXML.php y el código que contendrá es el siguiente:

<?
header("Content-type:text/xml");
echo "<?xml version=\"1.0\" encoding=\"utf-8\" ?>";
echo "<Informacion>";
echo "<Cliente>".$_SERVER['HTTP_USER_AGENT']."</Cliente>";
echo "<Software>".$_SERVER['SERVER_SOFTWARE']."</Software>";
setlocale ( LC_TIME, 'spanish' );
echo "<Fecha>".strftime("%A %d de %B del %Y")."</Fecha>";
echo "<Hora>".strftime("%H:%M:%S")."</Hora>";
echo "</Informacion>";
?>

Al analizarlo vemos que la primer línea que aparece es la función header:
header("Content-type:text/xml");
En ésta se indica que el tipo de contenido que enviará el servidor Web como respuesta es un archivo XML, es decir que declaramos el tipo de salida que recibirá el navegador.

El resto del código consiste en realizar la creación del contenido de éste archivo XML, iniciando con:
echo "<?xml version=\"1.0\" encoding=\"utf-8\" ?>";
que es un encabezado que declara la versión de XML que estamos utilizando, así como la codificación de los caracteres utilizada, en este caso utf-8. El resto del código crea cada uno de los elementos del documento XML, que representarán los campos con la información que desde un inicio teniamos pensado solicitar al servidor Web, y cada uno de ellos corresponde a los elementos que será accedido desde Javascript en el cliente.

Al almacenar el archivo PHP en el servidor Web podemos pasar a la prueba, si seguimos cada uno de los pasos indicados aquí, la respuesta que obtendremos será similar a esta:



Conclusiones

El punto central de este ejercicio es demostrar como desde PHP se puede mandar un conjunto de datos en XML, y procesarlos en Javascript pudiendo discriminarlo para desplegarlos de manera que se respete la estructura original. Esta práctica es importante porque utilizando los mismos principios, es posible mandar información estructurada en mayores volumnes ,desde el servidor hasta el programa cliente en Javascript. Se pondrían mandar, por ejemplo una serie de registros que representen los renglones de una tabla en una base de datos, o que representen una serie de mensajes generados que describan el resultado de un proceso que ocurrió en el servidor. Entonces, debido a la utilidad que representan las respuestas XML desde el servidor, este será el estándar que utilizaremos para entregar las respuestas a nuestros clientes Ajax, en los ejemplos que seguirán de ahora en adelante.

En los siguientes post veremos como refactorizar el código tanto del cliente como del servidor para automatizar más el manejo de XML, además haremos una introducción al manejo de bases de datos combinando Ajax con PHP.

Hasta la próxima.