28.8.06

Arquitectura Cliente-Servidor con AJAX y JSON

Programar aplicaciones con AJAX nos permite crear interfaces de usuario (UI) mucho mas rápidas. En esta nota mostraremos como además permite separar en capas los componente de nuestras aplicaciones para que sean mas fáciles de modificar y
mantener.

Hace cinco años una aplicación web era esencialmente un conjunto de páginas HTML con códigos PHP intercalados que a su vez escribían
mas tags HTML. El diseño lo hacíamos con tags como <table>, <font>, <b> y otros. A la hora de hacer modificaciones de diseño, de base de datos o de interface teníamos que modificar una ensalada de códigos donde todo estaba mezclado.

diagrama-de-capas-con-ajax-y-json.jpg
Hoy podemos combinar AJAX, CSS, PHP y Javascript para armar aplicaciones manteniendo el servidor, la interface de usuario y el diseño gráfico como componentes independientes de una aquitectura cliente-servidor, además de crear interfaces
con respuesta mas rápida.
De acuerdo a la forma de programar las aplicaciones AJAX las podemos dividir en dos: clientes livianos y clientes pesados.
Los cliente livianos se llaman asi porque tienen relativamente poco código del lado del cliente. Generalmente una llamada AJAX que al responder ejecuta una función que reemplaza el contenido de un DIV por el texto que devolvió el servidor. Es lo primero que la gente hace para implementar el "look" ajax en un programa. Y es lo mas parecido a seguir generando html desde el lenguaje de servidor (PHP, ASP) pero sin hacer refresh entre páginas.

Los clientes pesados obviamente son lo contrario, tienen mas lógica del lado del cliente. Hoy examinaremos un ejemplo de cliente pesado para mostrar como se puede mantener una
separación en capas en nuestras aplicaciones de forma natural.
La primera separación que haremos será dejar toda la lógica del presentación del lado del cliente. El código PHP (o el lenguaje que usemos en el servidor) tiene que estar libre de tags HTML. Si los datos recibidos deben ser procesados, por ejemplo para formar filas de una tabla, es el cliente quien debe generar el HTML a través de Javascript. Pero para esto es necesario que el código de nuesta interface de
usuario reciba del servidor algo que pueda entender mas allá de cadenas de texto y lo resolveremos utilizando JSON, un nuevo formato de intercambio de datos con muchas ventajas sobre XML.

El código PHP tiene que estar libre de tags HTML


En el servidor solo programaremos web services que realizan acciones o devuelven datos. Será una arquitectura de servicios y no un despachador de páginas.
ejemplo1.png

(ejemplo1.png) La aplicación antes que el usuario haga click para llenar la lista con datos.

ejemplo1.png

(ejemplo2.png) AJAX toma la lista de datos del servidor y escribe los datos dentro de la lista.

¿Que hace el ejemplo?
La aplicación que desarrollamos es un cliente HTML que cuando el usuario hace click sobre un texto pide al servidor los datos de una tabla MySQL. El servidor devuelve un arreglo y el cliente – en Javascript – le da formato y lo escribe en la página. Sin refrescar la pantalla por supuesto.


Capas de la aplicacion

Estructura: HTML
En nuesta aplicación el archivo .html tendrá la función de definir bloques que contendrán datos u otros bloques y eventos que pueden ocurrir el la interface. Los bloques no son mas que DIVs con un nombre para identificarlos. Definiremos una página con un botón (un div que dice “click aquí”) y una lista (otro div) que será rellenada con los datos obtenidos desde el servidor.

<html>
<script src="/javascripts/prototype.js"
type="text/javascript"></script>
<script src="aplicacion.js" type="text/javascript"></script>
<script>
</script>
<body>
<h1>Prueba de AJAX!</h1>
<div id="Boton" onClick="HicieronClick()">Hace click sobre este texto para llenar la lista</div>
<br>
<div id=lista>
En este div se van a mostrar los datos que se reciben por AJAX.
</div>
</body>
</html>


Archivo: ajax.html

Programación de la UI: Javascript

La programación del cliente está contenida íntegramente en uno o mas archivos .js
Aunque como lenguaje Javascript es algo limitado se lo puede ampliar utilizando una librería como Prototype que facilita mucho las cosas. En nuestro ejemplo la llamada AJAX la realizamos con un objeto Ajax.Request , que es parte de Prototype.
Definimos dos funciones: la que es llamada en el evento onClick del DIV “boton” y la que es llamada cuando el servidor contesta el llamado AJAX.

function HicieronClick() {
var opciones = {
//función a llamar cuando reciba la respuesta
onSuccess: function(t) {
datos = eval(t.responseText);

procesar(datos);
}
}
new Ajax.Request('/pruebas/datos2.php',opciones);
}

function procesar(datos) {
// guardo el div donde voy a escribir los datos en una variable
contenedor = document.getElementById("lista");

texto = "";
//Itero sobre los datos que me pasaron como parámetro
for (var i=0; i < datos.length; i++) {
dato = datos[i];
texto += "Dato numero:"+i+" - campo 1:"+dato.campo1;
texto += " campo2:"+dato.campo2+"<br>";
}
//Escribo el texto que formé en el div que corresponde
contenedor.innerHTML = texto;
}

aplicacion.js

Para lograr una mayor separación entre estructura (HTML) y código podríamos no especificar el evento onClick dentro del código html y en vez asociar el evento desde JavaScript con una función init() y el objeto Event de la librería Prototype. Si esto parece chino, no te preocupes que lo desarrollaré en otro artículo mas adelante.
Sigamos con el ejemplo.
La función HicieronClick(), que se ejecuta porque lo indicamos en el evento onClick del DIV “Boton”, es la que se encarga de hacer la llamada AJAX.
Primero guardamos en la variable opciones los parámetros necesarios. Como es un proceso asincrónico le pasamos una función en línea:

onSuccess: function(t) {
datos = eval(t.responseText);

procesar(datos);
}

Esta función toma los datos recibidos del servidor (t.responseText) los procesa y luego llama a procesar() pasando los datos como parámetro. Mas adelante veremos por
qué usamos la llamada a eval().

La función procesar() obtiene una referencia al DIV “lista” dentro del documento HTML y la guarda para luego escribir los datos ahi. Lo hace llamando a
document.getElementById("lista").
Luego itera sobre el arreglo de datos que recibió y va creando un texto con una línea por cada elemento, concatenando el índice y los dos campos que recibe del
servidor. Además agrega un tag “<br>” después de cada
fila para que al mostrarlo queden uno en cada línea.
Lo que hace es muy simple, pero nos da una idea de como manipular datos recibidos desde el servidor antes de mostrarlos. En vez de solo concatenarlos podríamos crear un DIV por cada fila, asignarles clases en forma condicional, o cualquier otra cosa.
El último paso de procesar es tomar el texto generado en la variable texto y escribirlo en el DIV de destino. Esto se logra con:
contenedor.innerHTML = texto;

JSON
Vamos a volver un segundo sobre la función que recibe los datos del servidor y por qué hace un eval() de lo que recibe.
Como dijimos al principio, los clientes pesados (o ricos, para no ser negativos) en vez de recibir del servidor texto ASCII o HTML y mostrarlo reciben datos y los procesan. Esos datos pueden ser enteros, caracteres, arreglos o de cualquier otro tipo. La pregunta que surge es ¿como devolver desde PHP un arreglo de manera que Javascript lo interprete como tal?
Hasta ahora la solución era codificarlo en XML. De ahí viene la “X” en AJAX. El problema con XML es que no es fácil de leer por un humano, no es facil de codificar y procesar y además tiene mucho overhead.
Como alternativa al uso de XML surgió JSON, que significa Javascript Object Notation. Para codificar en JSON un dato se lo escribe como su representación en código
Javascript. Por ejemplo, para transmitir una cadena se la encierra entre comillas, y un arreglo en JSON se escribe asi: [“primer elemento”, “otro elemento”, 3, 4 , “otros datos” ]

Tiene muchas ventajas. Es simple escribir una rutina que codifique en JSON en cualquier lenguaje, es fácil de leer, es una representación compacta y muy fácil de procesar.
En Javascript es trivial decodificarlo, ya que un mensaje JSON es código Javascript válido. Por eso podemos ejecutar eval(cadena) para obtener el dato correspondiente.
Para codificar o decodificar JSON desde PHP podemos utilizar la librería PHP-JSON o escribir nuestra propia rutina.

Diseño gráfico: CSS
Hasta ahora nuesta aplicación tiene un problema: es horrible. Siguiendo con la filosofía de poner cada cosa en su lugar reservamos todo lo relacionado al diseño
gráfico al archivo CSS correspondiente.
Deberíamos armar el HTML definiendo areas de acuerdo a la función que cumplen, como ser contenedores de otras areas o botones, y defir la clase de cada DIV (el atributo
class=“...” ), pero olvidarnos de tags como <font>, <b> y de usar <table> pasa posicionar elementos en las páginas. No tenemos lugar para hacer un tutorial de CSS aquí, pero hay muchos ejemplos de como posicionar elementos en una página utilizando CSS.
Un recurso interesante en CSS Zen Garden (www.csszengarden.com) donde se muestra como la misma página HTML puede tomar infinidad de apariencias solo cambiando el CSS en uso. El site de ESPN (www.espn.com) también recurre a CSS para realizar un
formato de varias columnas de noticias.
Trabajando de de esta forma nuestra aplicación puede sufrir cambios de diseño gráfico sin tener que tocar los archivos HTML ( ni hablar de los archivos PHP).

Usando un archivo CSS cambiamos la ubicación y colores de los DIVs


screenshot-con-css.png



zen-garden-con-css.png

(Zen Garden: con CSS.png) Ejemplo de diseño
gráfico integral con CSS: CSS Zen Garden


zen-garden-sin-css.png

(Zen Garden: sin CSS.png) Si deshabilitamos CSS vemos que la
página está compuesta exclusivamente por texto


Comunicación con el servidor: JSON
Como dijimos anteriormente para que la aplicación pueda procesar los datos que recibe del servidor necesitamos que los entienda. Si el servidor devolviera datos XML los podríamos procesar desde Javascript. Ese es el espíritu original de AJAX. Pero XML tiene algunos inconvenientes: no es fácil de leer por una persona, no es facil de generar ni de procesar y tiene mucho overhead de transmisión. Para resolver estos problemas se inventó JSON, que significa Javascript Object Notation, aunque no se limita a Javascript. Consiste en codificar los datos como la instrucción que los representa en Javascript. Básicamente si el servidor quiere devolver un string debería escribir “este es un string” (incluyendo las comillas) y si quiere devolver un arreglo
podría transmitir: ["Hola! Soy un elemento!", "Dato número 2",
"ultimo dato"] La utilidad de esta codificación es que es fácil
de leer por humanos, compacta, trivial para codificar y desde Javascript se la decodifica con un simple eval().
Por ejemplo:
miarreglo = eval(respuestadelservidor);

Una de las empresa que adoptó JSON para sus web-services públicos es Yahoo!, que da la opción de utilizar una interface SOAP (XML) o JSON.
En nuestro ejemplo en PHP utilizaremos la librería PHP-JSON para codificar las respuestas.

Otros lenguajes
En este artículo elegimos programar el servidor en PHP, pero al estructurar la aplicación como cliente servidor podemos implementar los web services en cualquier otro lenguaje. Hay muchas librerías para codificar los datos en JSON disponibles.
Implementar una propia también es fácil.

Servidor: PHP
Dejamos la capa del servidor para el final intencionalmente porque es la parte mas simple del sistema.
Como elegimos una arquitectura de servicios el servidor solo tiene que procesar los datos que recibe, realizar alguna tarea y devolver datos codificados en JSON.
La mayor parte del código del ejemplo se ocupa de realizar la consulta a MySQL para obtener los datos.
<?

//Creo el objeto $json para codificar datos mas adelante
require_once('JSON.php');
$json = new Services_JSON();


$link = mysql_connect('localhost', 'root', '');
mysql_select_db('mi_base');

$query = 'SELECT campo1, campo2 FROM mi_tabla';
$result = mysql_query($query) ;

$datos = array();

//lleno el array $datos con el resultado de la consulta a MySQL:
while ($line = mysql_fetch_array($result, MYSQL_ASSOC)) {
$datos[]=$line;
}

print $json->encode($datos);

mysql_free_result($result);
mysql_close($link);
?>


datos2.php

¿Que sigue?
El ejemplo que hicimos es muy simple pero sirve para ilustrar la forma de estructurar una aplicación como un cliente programado con HTML y Javascript y un servidor en PHP que proporciona servicios web, y ya no como muchas páginas enlazadas.
El siguiente paso sería programar un servicio web que tome parámetros y agregar mas servicios web al servidor. Una forma de hacerlo es proporcionar una URL distinta para cada
función que quiero llamar desde AJAX, separando así cada web service en un archivo PHP diferente y organizándolos por directorios. Otra forma es crear un único archivo PHP que tome de la variable $_REQUEST el nombre de la función que se quiere llamar.

La elección de PHP para el ejemplo se debe a que es el lenguaje mas difundido para programación web, pero nuestra arquitectura permite reemplazar el servidor por cualquier otro que implemente los mismos web services con JSON. Podríamos haberlo programado sobre ASP .NET, Python, Perl, C (como CGI) o cualquier otro lenguaje y no tendríamos que cambiar nada en el cliente. El modelo también es independiente de la
plataforma elegida.

Código fuente de los ejemplos:
Los ejemplos están en un repositorio SVN en google, pueden verlos con un browser en la dirección: http://ejemplosajax.googlecode.com/svn/trunk/