Ejemplo simple con Ajax - parte final

Desarrollo de software, Web July 30th, 2007

La aplicación de ejemplo fue producto de mi primer par de horas de contacto con Ajax y Prototype así que tal vez no sea la mejor de las soluciones, sin embargo como todo es suceptible de ser mejorado. Se reciben comentarios al respecto.

La aplicación de ejemplo y los archivos fuente pueden ser descargados del siguiente URL: http://demo.jorgeivanmeza.com/Prototype/ajax1/.

Tags: , , ,

Ejemplo simple con Ajax - Cuarta parte

Desarrollo de software, Web July 19th, 2007

La parte final del ejemplo corresponde al código PHP que realiza en realidad las tareas de inserción, búsqueda y listado de los registros en la base de datos.

El archivo config.php define la información requerida para la ejecución de la aplicación, en este ejemplo concreto determina la información de conexión a la base de datos.

$config["bd"]["tipo"] = "sqlite";
$config["bd"]["servidor"] = "localhost";
$config["bd"]["nombre"] = "data/database.db";
$config["bd"]["usuario"] = "nobody";
$config["bd"]["contrasena"] = "nothing";

Incluye una función de apoyo para generar rápidamente el DSN de conexión.

function construirDSN()
{
global $config;

$dsn = $config["bd"]["tipo"]."://".$config["bd"]["usuario"].":".
$config["bd"]["contrasena"]."@".$config["bd"]["servidor"]."/".
$config["bd"]["nombre"];

return $dsn;
}

Incluye también una ampliación en ejecución del include_path de PHP para acceder sin problemas de rutas a las clases de Creole.

ini_set("include_path", ini_get("include_path") . PATH_SEPARATOR . 'lib/creole');

El archivo buscar.php realiza la búsqueda del registro cuyo documento corresponda con el especificado por el usuario. Incluye los archivos de configuración y de Creole para el acceso a la base de datos.

require_once('config.php');
require_once('creole/Creole.php');

Se requiere de la variable doc obtenida a través de los parámetros GET la cual representa el documento a buscarse. Se verifica que la variable exista, en caso contrario se termina la ejecución con un error 500.

if (!isset($_GET['doc']))
header("HTTP/1.0 500 El documento no fue correctamente especificado.");

$documento = htmlspecialchars($_GET['doc'], ENT_QUOTES);

Se realiza la conexión a la base de datos, en caso de error se termina la ejecución con un error 500.

$dsn = construirDSN();

$conn = Creole::getConnection($dsn, Creole::COMPAT_ASSOC_LOWER |
Creole::COMPAT_RTRIM_STRING);

if ($conn == null)
header("HTTP/1.0 500 Tuve problemas abriendo la base de datos.");

Se realiza la consulta a la base de datos basada en el documento especificado por el usuario. Nuevamente, en caso de fallar la consulta, se termina la ejecución con un error 500.

$sql = "SELECT documento, nombres, apellidos, email
FROM usuario
WHERE documento='{$documento}'";

try
{
$rs = $conn->executeQuery($sql);
$rs->next();
}
catch (SQLException $e)
{
header("HTTP/1.0 500 Tuve problemas ejecutando la consulta.");
}

Se verifica que la consulta haya retornado una registro como respuesta, en caso contrario se le indica al usuario.

$count = $rs->getRecordCount();

if ($count == 0)
header("HTTP/1.0 500 El documento consultado no existe en la base de datos.");

En caso de éxito, se obtiene la información del registro encontrada en la base de datos y se construye el mensaje en formato JSON para ser remitida a la función en JavaScript que la mostrará en el formulario.

$document = htmlspecialchars_decode ($rs->get('documento'), ENT_QUOTES);
$name = htmlspecialchars_decode ($rs->get('nombres'), ENT_QUOTES);
$lastname = htmlspecialchars_decode ($rs->get('apellidos'), ENT_QUOTES);
$email = htmlspecialchars_decode ($rs->get('email'), ENT_QUOTES);

$mensaje = "{
'documento': '{$document}',
'nombres': '{$name}',
'apellidos': '{$lastname}',
'email': '{$email}'
}";

Se finaliza la información de conexión a la base de datos, se notifica del éxito del procedimiento y se envía la información de vuelta al solicitante.

$rs->close();

$conn->close();

header("HTTP/1.0 200 Documento encontrado exitosamente.");

echo $mensaje;

El archivo agregar.php inserta la información determinada por el usuario en la forma como un registro en la base de datos. Incluye los mismos archivos de soporte del archivo anterior.

Requiere que se especifiquen la información del documento, nombres, apellidos y dirección de correo electrónico del registro que se va a agregar. Verifica la existencia de esta información y termina la ejecución en caso de faltar cualquiera de las variables. En caso de éxito obtiene la información que va a ser agregada.

if (!isset($_POST['doc']) || !isset($_POST['nom']) ||
!isset($_POST['apl']) || !isset($_POST['ema']))
header("HTTP/1.0 500 El registro no fue correctamente especificado.");

$documento = htmlspecialchars($_POST['doc'], ENT_QUOTES);
$nombres = htmlspecialchars($_POST['nom'], ENT_QUOTES);
$apellidos = htmlspecialchars($_POST['apl'], ENT_QUOTES);
$email = htmlspecialchars($_POST['ema'], ENT_QUOTES);

Se realiza la conexión a la base de datos, en caso de error se termina la ejecución con un error 500.

$dsn = construirDSN();

$conn = Creole::getConnection($dsn, Creole::COMPAT_ASSOC_LOWER |
Creole::COMPAT_RTRIM_STRING);

if ($conn == null)
header("HTTP/1.0 500 Tuve problemas abriendo la base de datos.");

Se prepara la sentencia INSERT de SQL para agregar la información especificada a la base de datos.

$sql = "INSERT INTO usuario (documento, nombres, apellidos, email)
VALUES (?, ?, ?, ?)";

$stmt = $conn->prepareStatement($sql);

$stmt->setString(1, $documento);
$stmt->setString(2, $nombres);
$stmt->setString(3, $apellidos);
$stmt->setString(4, $email);

Se realiza la ejecución de la misma.

$count = 0;

try
{
$count = $stmt->executeUpdate();
}
catch (SQLException $e)
{
header("HTTP/1.0 500 Tuve problemas insertando el nuevo registro.");
}

if ($count == 0)
header("HTTP/1.0 500 No fue posible agregar el registro en la base de datos.");

En caso de éxito, finaliza la información de conexión a la base de datos y notifica del éxito del procedimiento.

$stmt->close();

$conn->close();

header("HTTP/1.0 200 El registro fue agregado exitosamente.");

El último archivo que conforma el ejemplo es listar.php que despliega la lista de registro almacenados en la base de datos al presionar el botón Registros. Se incluyen los mismos archivos de los códigos anteriores y se realiza la conexión a la base de datos de igual manera.

Se consultan los registros almacenados en la base de datos.

$sql = "SELECT documento, nombres, apellidos, email
FROM usuario
ORDER BY apellidos, nombres";

try
{
$rs = $conn->executeQuery($sql);
$rs->next();
}
catch (SQLException $e)
{
header("HTTP/1.0 500 Tuve problemas ejecutando la consulta.");
}

Se verifica que la consulta retorne información, en caso contrario se notifica al solicitante.

$count = $rs->getRecordCount();

if ($count == 0)
header("HTTP/1.0 500 No se encuentran registros en la base de datos.");

En caso de éxito se prepara la información tal y como se va a presentar en la forma, en este caso dentro de una tabla HTML.

$mensaje = "<table cellspacing='3' cellpadding='2' border='1'>";

$mensaje .= "<tr bgcolor='yellow' align='center'><td><b>Documento</b></td>" .
"<td><b>Nombres</b></td><td><b>Apellidos</b></td>" .
"<td><b>Email</b></td></tr>\n";

do
{
$mensaje .= "<tr>";
$mensaje .= "<td>{$rs->get('documento')}</td>";
$mensaje .= "<td>{$rs->get('nombres')}</td>";
$mensaje .= "<td>{$rs->get('apellidos')}</td>";
$mensaje .= "<td>{$rs->get('email')}</td>";
$mensaje .= "</tr>\n";
} while($rs->next());

$mensaje .= "</table>";

Se finaliza la información de conexión a la base de datos, se notifica del éxito del procedimiento y se envía la información de vuelta al solicitante.

$rs->close();

$conn->close();

header("HTTP/1.0 200 Listado de registros generado exitosamente.");

echo $mensaje;

Tags: , , ,

Ejemplo simple con Ajax - Tercera parte

Desarrollo de software, Web July 7th, 2007

Modelo de llamados a procedimientos JavaScript y PHP para la ejecución de código con AJAX

La secuencia de llamados es la siguiente.

[1] Un botón en una página HTML ejecuta a una función JavaScript según la especificación del manejo de su evento OnClick.

[2] La función (Manejador de Acción en el diagrama) especifica el URL del PHP a ejecutarse, el método de envío de parámetros (POST o GET), prepara los parámetros necesarios (mensaje en formato GET) y determina cuales funciones JavaScript deberán ejecutarse en el caso en que la ejecución del programa PHP envíe la señal de que ha terminado exitosamente o que ha fracasado.

[3] El programa PHP (Implementación) es ejecutado previo envío de los parámetros con la información necesaria para su ejecución. La aplicación ejecutada puede tomar ventaja de todas las facilidades que el lenguaje nativo ofrece, incluyendo el acceso a bases de datos.

El programa PHP envía a la salida estándar la información que requiere enviar como respuesta al procedimiento AJAX que inicio su ejecución. Además puede indicar su estado de éxito o de fracaso manipulando los códigos HTTP de respuesta (2xx es éxito, cualquier otro es fracaso). En PHP esto se realiza utilizando la función header con la información apropiada. Su llamado debe ser previo a cualquier envío de información (echo, printf, etc.) por parte de código PHP. Si nose especifica ninguna respuesta, se asume la 200 (éxito).

[4] Según la respuesta obtenida por la aplicación PHP se ejecuta la función JavaScript Manejador de Éxito o Manejador de Fracaso según se especificó en la definición del Manejador de Acción.

Estas funciones tienen acceso a la información producida por la aplicación PHP y por el código de respuesta de la misma a través de las siguientes funciones.

  • xmlHttpRequest.responseText: Información suministrada por la aplicación PHP.
  • xmlHttpRequest.responseXML: Igual que responseText representando la respuesta en una estructura XML.
  • xmlHttpRequest.statusText: Código de respuesta de la aplicación PHP.
  • xmlHttpRequest.statusText: Información del código de respuesta de la aplicación PHP.

La ejecución de las funciones mencionadas determina la actualización de la interfaz de usuario (HTML) según la respuesta obtenida.

A continuación se describen los procedimientos y código fuente requerido para realizar las funciones de búsqueda, inserción y listado de registros.

* Búsqueda de registros.

[1] El botón b_buscar del formulario myForm ejecuta la función buscar cuando su evento OnClick es lanzado. La función mencionada recibe como parámetro el documento del registro a buscar.

[2] La función JavaScript buscar(doc) procesa la solicitud de la siguiente manera.

Crea una cadena con los datos requeridos para procesar correctamente la información (params).
Establece la ubicación (url) de la aplicación PHP a ejecutarse.
Establece el método (method) para transmitir esta información.

var params = "doc=" + doc;
var url = "buscar.php";
var method = "get";

Ejecuta el llamado a la aplicación utilizando los valores definidos anteriormente y definiendo las funciones doExitoBusqueda y doFracasoBusqueda para procesar los casos de éxito y fracaso respectivamente.

var ajax = new Ajax.Request (url,
{
method: method,
parameters: params,
onSuccess: doExitoBusqueda,
onFailure: doFracasoBusqueda
}
);

[3] En este momento la aplicación PHP, que será discutida posteriormente, es ejecutada.

En caso de que suceda algún tipo de error es enviado el mensaje 500 junto con un texto alusivo al error. En caso de éxito es enviado un mensaje 200, de igual manera se envía la información respuesta imprimiendo a esta a partir de echo.

[4] En caso de error se ejecuta la función doFracasoBusqueda que obtiene el mensaje asociado al error (xmlHttpRequest.statusText) y lo muestra en el campo dispuesto para tal fin, valiéndose de la función mostrarMensajeError.

Como el registro no pudo ser encontrado, la función limpia el valor de los campos nombres, apellidos e email el puede contener en ese momento valores imprecisos.

function doFracasoBusqueda(xmlHttpRequest, responseHeader)
{
mensaje = xmlHttpRequest.statusText;

mostrarMensajeError(mensaje, "error");

document.getElementById("t_nombres").value = "";
document.getElementById("t_apellidos").value = "";
document.getElementById("t_email").value = "";
}

En caso de éxito se ejecuta la función doExitoBusqueda que muestra un mensaje de estado informando al usuario del éxito de la búsqueda, convierte la información recibida de la ejecución de la aplicación PHP (xmlHttpRequest.responseText) que viene en formato JSON y la convierte utilizando el parser de Prototype (evalJSON) para convertirlo en una clase cuyos campos son ubicados en sus atributos. Esta información recibida es transmitida a los campos de texto correspondientes para ser presentados al usuario.

function doExitoBusqueda(xmlHttpRequest, responseHeader)
{
var response = xmlHttpRequest.responseText;
var mensaje = xmlHttpRequest.statusText;

mostrarMensajeError(mensaje, "exito");

var data = response.evalJSON();

document.getElementById("t_documento").value = data.documento;
document.getElementById("t_nombres").value = data.nombres;
document.getElementById("t_apellidos").value = data.apellidos;
document.getElementById("t_email").value = data.email;
}

* Inserción de registros.

[1] El botón b_agregar del formulario myForm ejecuta la función agregar cuando su evento OnClick es lanzado. La función mencionada recibe como parámetro la forma donde se encuentran ubicados los campos que contienen la información relativa al registro a insertar.

[2] La función JavaScript agregar(forma) procesa la solicitud de la siguiente manera.

Crea una cadena con los datos requeridos para procesar correctamente la información (params).
Establece la ubicación (url) de la aplicación PHP a ejecutarse.
Establece el método (method) para transmitir esta información.

var params = "doc=" + forma.t_documento.value +
"&nom=" + forma.t_nombres.value +
"&apl=" + forma.t_apellidos.value +
"&ema=" + forma.t_email.value;
var url = "agregar.php";
var method = "post";

Ejecuta el llamado a la aplicación utilizando los valores definidos anteriormente y definiendo las funciones doExitoAgregar y doFracasoAgregar para procesar los casos de éxito y fracaso respectivamente.

var ajax = new Ajax.Request (url,
{
method: method,
postBody: params,
onSuccess: doExitoAgregar,
onFailure: doFracasoAgregar
}
);

[3] En este momento la aplicación PHP, que será discutida posteriormente, es ejecutada.

[4] En caso de error se ejecuta la función doFracasoAgregar que obtiene el mensaje asociado al error y lo muestra en el campo dispuesto para tal fin, valiéndose de la función mostrarMensajeError.

function doFracasoAgregar(xmlHttpRequest, responseHeader)
{
mensaje = xmlHttpRequest.statusText;

mostrarMensajeError(mensaje, "error");
}

En caso de éxito se ejecuta la función doExitoAgregar que muestra un mensaje de estado informando al usuario del éxito del proceso de inserción.

function doExitoAgregar(xmlHttpRequest, responseHeader)
{
mensaje = xmlHttpRequest.statusText;

mostrarMensajeError(mensaje, "exito");
}

* Generar un listado de registros.

[1] El botón b_listar del formulario myForm ejecuta la función listar cuando su evento OnClick es lanzado.

[2] La función JavaScript listar() procesa la solicitud de la siguiente manera.

Determina a partir del estado de la variable global (mostrandoListado) si el listado se encuentra actualmente en pantalla (true) y en cuyo caso debe ocultarse o si por el contrario, el listado se encuentra oculto (false) y deberá presentarse al usuario.

En caso de requerirse que se oculte, el contenido del DIV con el identificador listado_registros es vaciado.

if (mostrandoListado)
{
mostrandoListado = false;

document.getElementById("listado_registros").innerHTML = "";

return;
}

En el caso contrario, cuando es necesario desplegar la lista de registro en la página, se realizan los siguientes pasos.

Establece la ubicación (url) de la aplicación PHP a ejecutarse.
Establece el método (method) para transmitir esta información.

mostrandoListado = true;

var url = "listar.php";
var method = "get";

Ejecuta el llamado a la aplicación especificando el DIV donde se mostrará la tabla (listado_registros), los valores definidos anteriormente y definiendo las funciones doExitoListar y doFracasoListar para procesar los casos de éxito y fracaso respectivamente.

var ajax = new Ajax.Updater ("listado_registros",
url,
{
method: method,
onSuccess: doExitoListar,
onFailure: doFracasoListar
}
);

En ambos casos se modifica el valor de la variable mostrandoListado a su negación para mantener sincronizado el sistema.

Nótese por favor que en este último caso se utilizo Ajax.Request en lugar de Ajax.Updater. La diferencia entre los dos radica en que el primero permite especificar que funciones JavaScript se deberán ejecutar según el éxito o el fracaso de la aplicación ejecutada, mientras que el segundo permite además especificar el id de un DIV al cual se redireccionará automáticamente la información recibida de parte de la aplicación PHP.

[3] En este momento la aplicación PHP, que será discutida posteriormente, es ejecutada.

[4] En caso de error se ejecuta la función doFracasoListar que obtiene el mensaje asociado al error y lo muestra en el campo dispuesto para tal fin, valiéndose de la función mostrarMensajeError.

function doFracasoListar(xmlHttpRequest, responseHeader)
{
mensaje = xmlHttpRequest.statusText;

mostrarMensajeError(mensaje, "error");
}

En caso de éxito se ejecuta la función doExitoListar que muestra un mensaje de estado informando al usuario del éxito de la presentación de la información.

function doExitoListar(xmlHttpRequest, responseHeader)
{
mensaje = xmlHttpRequest.statusText;

mostrarMensajeError(mensaje, "exito");
}

Como última etapa del desarrollo de la aplicación, se deberá continuar con la especificación de las aplicaciones PHP (buscar.php, agregar.php y listar.php) las cuales son las responsables realmente de procesar la información por solicitud del usuario y de recuperarla o en la base de datos según se requiera.

Tags: , , ,

Ejemplo simple con Ajax - Segunda parte

Desarrollo de software, Web July 6th, 2007

Diagrama Ajax1

Estructura:

[1] El formulario tiene 4 campos: Documento de identidad, nombres, apellidos y dirección de correo electrónico que corresponden 1:1 con los campos de la tabla usuario de la base de datos.

[2] En la parte superior hay un espacio para los mensajes de error.

[3] En la parte inferior hay un espacio para mostrar la tabla de registros.

[4] A continuación del campo: documento de identidad, hay un botón Buscar que permite indagar sobre la existencia de un registro con dicho documento.

[5] En la parte inferior está el botón Agregar que permite insertar la información del usuario ubicada en el formulario a la base de datos.

[6] También se encuentra un botón Registros que permite mostrar u ocultar la tabla de registros con la información almacenada hasta el momento en la base de datos.

Flujo:

Si se presiona [4] se consulta la base de datos en busca del documento especificado. En caso de encontrarse una coincidencia, su información se muestra en [1], en caso contrario se muestra el mensaje de error en [2].

Si se presiona [5] se intenta realizar la inserción de la información en la tabla usuario de la base de datos. En caso de tener éxito o fracaso durante la transacción, se muestra un mensaje adecuado en [2].

Si se presiona [6] se muestran los registros contenidos en la base de datos en [3]. En caso de no existir registros se muestra un mensaje de error apropiado en [2].

Implementación:

Teniendo esto claro se procedió con la implementación del formulario en HTML en el archivo form.html.

Se contó con 4 áreas. Una para la inclusión de los archivos JavaScript: prototype.js requerido por el framework de Prototype y form.js que contiene las funciones a definirse próximamente.

<script language="javascript" src="lib/prototype/prototype.js"></script>
<script language="javascript" src="form.js"></script>

La segunda sección representa el espacio reservado para los mensajes de error.

<div id="mensaje_error"></div>

La tercer sección define el formulario (myForm) como tal. Las longitudes de los campos de texto (maxlength) deberán coincidir con los tamaños de los campos definidos en la base de datos.

<form name="myForm">
<table>
<tr>
<td>
<b>Documento</b>
</td>
<td>
<input type="text" id="t_documento" value="" size="30"
maxlength="10" />
</td>
<td>
<input type="button" id="b_buscar" value="Buscar"
onClick="buscar(document.myForm.t_documento.value)" />
</td>
</tr>
<tr>
<td>
<b>Nombres</b>
</td>
<td colspan="2">
<input type="text" id="t_nombres" value="" size="30"
maxlength="60" />
</td>
</tr>
<tr>
<td>
<b>Apellidos</b>
</td>
<td colspan="2">
<input type="text" id="t_apellidos" value="" size="30"
maxlength="60" />
</td>
</tr>
<tr>
<td>
<b>Dirección de Correo</b>
</td>
<td colspan="2">
<input type="text" id="t_email" value="" size="30"
maxlength="128" />
</td>
</tr>
</table>

<input type="button" id="b_agregar" value="Agregar"
onClick="agregar(document.myForm) "/>

<input type="button" id="b_listar" value="Registros"
onClick="listar() "/>
</form>

Nótese que el evento onClick de los botones Buscar, Agregar y Registros es manejado por las funciones JavaScript buscar(documento), agregar(forma) y listar() respectivamente, las cuales se definirán próximamente.

La cuarta y última sección de la interfaz de usuario representa el espacio reservado para desplegar la tabla de registros.

<div id="listado_registros"></div>

El siguiente paso corresponde al implementar las funciones JavaScript almacenadas en el archivo form.js que produzcan los resultados esperados en la presión de los botones de la forma. La implementación de estas funciones estará basada en la librería de Prototype.

Tags: , , ,

Ejemplo simple con Ajax - Primera parte

Desarrollo de software, Web July 6th, 2007

[...]

Me quedé pensando ... qué tan difícil puede ser implementar el formulario solicitado utilizando AJAX para implementarlo como una mejor experiencia para el usuario que tiene que digitar la información ? Así fue como el jueves me dí a la tarea de crear mi primera aplicación (de prueba) en AJAX que describo a continuación.

Para el desarrollo de la aplicación tuve las siguientes consideraciones.

  • En lugar del autocompletar propuesto incialmente continué con la propuesta del botón de Buscar, sin embargo utilizando AJAX para la comunicación de las consultas.
  • El lenguaje del lado del servidor será PHP.
  • Utilizar Prototype (http://www.prototypejs.org/) como framework facilitador para la implementación de los llamados AJAX.
  • Utilizar SQLite (http://www.sqlite.org/) como medio de almacenamiento.
  • Utilizar Creole (http://creole.phpdb.org/) como abstracción del acceso a bases de datos desde PHP.

Creé la siguiente estructura de directorios para mi proyecto.

/ Archivos HTML, PHP y JS.

/data Archivo de almacenamiento de la base de datos.

/lib Almacenamiento librerías de terceros descritas a continuación.

/lib/creole

/lib/prototype

/lib/sqlite

El primer paso fue la creación de la base de datos a partir de la siguiente especificación de tabla.

CREATE TABLE usuario (
documento CHAR(10) NOT NULL,
nombres CHAR(60) NOT NULL,
apellidos CHAR(60) NOT NULL,
email CHAR(128) NOT NULL,
PRIMARY KEY (documento)
);

Con los siguientes registros incluidos como ejemplo.

INSERT INTO usuario VALUES ('123', 'PEPE', 'PIMENTON', 'pepe@pimenton.com');
INSERT INTO usuario VALUES ('231', 'LAURA', 'LLANO', 'laura@llano.com');
INSERT INTO usuario VALUES ('321', 'MIGUEL', 'ARBELAEZ', 'miguel@arbelaez.com');
INSERT INTO usuario VALUES ('213', 'PATRICIA', 'PEREZ', 'patricia@perez.com');

Esto se realizó de la siguiente manera.

dos> cd RUTA\ajax1\data

dos> ..\lib\sqlite\sqlite.exe database.db < script.sql

Los comandos anteriores crean el archivo 'database.db' a patir de la ejecución del 'script.sql' el cual contiene lo mencionado anteriormente. Es de notar que el archivo de base de datos se crea en el directorio actual.

A continuación escribí la interfaz de usuario en HTML.

Tags: , , ,