Hacer algo cuando inicia o termina el evento AJAX con Prototype

Introducción.

De manera análoga a como hace poco había mostrado como manejar el evento de inicio y terminación de AJAX con jQuery para realizar algún tipo de acción específica como el mostrar un indicador de carga, ahora experimentaremos como hacerlo con el framework de Prototype el cual nuevamente estaré utilizando en el proyecto de los próximos meses.

Procedimiento.

Ajax.Responders.register({
    onCreate: function()
    {
        // An AJAX request has been initialized!
    },

    onComplete: function()
    {
        // An AJAX request has been completed!
    }
});

Adicionalmente hay otros eventos que pueden manejarse de igual manera onUninitialized, onLoading, onLoaded, onInteractive y onException, además de los ya mencionados onCreate y onComplete.

Enlaces.

Ejemplo rápido y simple de AJAX con PHP y PrototypeJS

Introducción.

Intentando recuperar mis neuronas que saben de Prototype para continuar por fin con uno de los proyectos que se encontraba en pausa permanente, el día de hoy me día a la breve tarea de recordar un poco la invocación asíncrona y la manipulación del DOM utilizando esta librería.  Para hacer una pequeña práctica decidí modificar el ejemplo de la calculadora que se basaba en jQuery y PHP para migrar su código de acuerdo con la especificación de Prototype.

Las modificaciones necesarias se centraron en la inclusión de la librería javascript en la página web (frontend) y en reescribir la invocación asíncrona de la aplicación web en PHP (backend).

Procedimiento.

Reemplazar la inclusión de la librería de jQuery por la de Prototype.  Para este caso se continúo utilizando el API de Google AJAX.

<script src="http://www.google.com/jsapi"></script>
<script>google.load("prototype", "1.6");</script>

Posteriormente se asoció el manejo del evento de presión del botón igual a la invocación de la función procesar, esto se realiza tan pronto como la estructura de la página (código HTML) se encuentra cargada completamente por el navegador.

/* Código a ejecutarse tan pronto como la
   página ha sido cargada por el navegador */

document.observe('dom:loaded', function()
{
    /* Asociar el evento de clic del botón 'igual'
       con la lógica del negocio de la aplicación */

    Event.observe('igual', 'click', procesar);
});

Finalmente se implementa la función procesar que realizará la invocación asíncrona del cálculo matemático.  Esta función consta de las siguientes partes.

  1. Información básica del requerimiento.
  2. Que hacer en caso de éxito.
  3. Que hacer en caso de fracaso.

El requerimiento incluye la siguiente información básica de conexión.

  • El URL de la aplicación remota a invocar (backend).
  • El método HTTP a utilizar.
  • Los parámetros de la página web a enviarse.
function procesar()
{
    new Ajax.Request('calcular.php',                                         /* URL a invocar asíncronamente */
    {
        method:       'post',                                                /* Método utilizado para el requerimiento */
        parameters:   $('formulario').serialize(true),                       /* Información local a enviarse con el requerimiento */

En caso de que la invocación asíncrona tenga un resultado exitoso se deberán realizar los siguientes pasos.

  • Mostrar un mensaje de éxito en color verde.
  • Desplegar el valor del resultado obtenido en el campo definido para tal fin.
        /* Que hacer en caso de ser exitoso el requerimiento */

        onSuccess: function(transport)
        {
            /* Cambiar el color del texto a verde */

            $('mensaje').setStyle('color: #0ab53a');

            /* Mostrar un mensaje informando el éxito sucedido */

            $('mensaje').update("Operación realizada exitosamente");

            /* Mostrar el resultado obtenido del cálculo solicitado */

            $('resultado').update(transport.responseText);
        },

En caso de que fracase el proceso de invocación asíncrona se deberán realizar los siguientes pasos análogos.

  • Mostrar el mensaje de error proveniente del servidor, en color rojo.
  • Limpiar cualquier resultado previo para evitar confusiones con la operación.
        /* Que hacer en caso de que sea fallido el requerimiento */

        onFailure: function(transport)
        {
            /* Cambiar el color del texto a rojo */

            $('mensaje').setStyle('color: #ff0e0e');

            /* Mostrar el mensaje de error */

            $('mensaje').update('Error: ' + transport.responseText);

            /* Limpiar cualquier resultado anterior */

            $('resultado').update('Error');
        }
    });
}

Enlaces.

Ejemplo de AJAX rápido y simple con PHP y jQuery

Introducción.

Muy probablemente usted esté aquí, leyendo este artículo porque desea aprender a utilizar AJAX con PHP de una manera muy simple y rápida.  Yo estoy aquí porque hoy no tengo sueño y que mejor manera de esperarlo que escribir un pequeño tutorial acerca del acceso asíncrono de contenidos web utilizando el framework de jQuery.

Para no tener que dividir el artículo en varias entregas vamos a desarrollar un ejemplo muy sencillo y clásico: la calculadora, sólo que a diferencia de la habitual esta realizará sus cálculos del lado del servidor y presentará sus resultados a través de llamados con AJAX.  La implementación Javascript asociada con el objeto AJAX (el objeto XMLHTTPRequest) no la realizaremos directamente sino que utilizaremos el framework de jQuery el cual simplifica y facilita enormemente el desarrollo en Javascript.  El lado del servidor, los cálculos de nuestra calculadora, se implementarán en el viejo y conocido PHP.  Por supuesto para implementar el nivel de presentación (página web) que recibe el cliente tendremos que hablar un poco de HTML y CSS.

Qué es AJAX ?

AJAX es el acrónimo de Asynchronous JavaScript and XML, un conjunto de tecnologías web que permiten que las aplicaciones web desde sus clientes se comuniquen y soliciten información al servidor de manera asíncrona sin interferir con el contenido y comportamientos de la página actual, es decir, sin refrescar (postback) la página web por completo.

Su implementación se basa en el objeto XMLHttpRequest o XHR el cual fue originalmente desarrollado por Microsoft y apareció por primera vez en 1999 como un control ActiveX de Internet Explorer 5, posteriormente los demás navegadores lo acogieron desarrollando sus propias implementaciones.

En términos generales el uso de AJAX mejora la usabilidad de las páginas permitiendo a los desarrolladores crear aplicaciones que se comportan de manera similar a las aplicaciones de escritorio.  Un ejemplo de esto son los clientes de correo web de última generación (como GMail) y las aplicaciones geográficas (como Google Maps).  También tiene sus desventajas como presentar una mayor dificultad para su depuración, control de favoritos e historial y que los buscadores web comúnmente no pueden acceder a sus contenidos para indexarlos.

Consulte mas información acerca de AJAX en Wikipedia.

Procedimiento.

Estos son los pasos generales que realizaremos para implementar nuestra pequeña aplicación.

  1. Crear la estructura básica de la página web.
  2. Implementar el formulario de la calculadora.
  3. Mejorar la presentación del formulario.
  4. Implementar la lógica del negocio de la calculadora.
  5. Incluír una referencia a la librería de jQuery en la página web.
  6. Implementar los llamados asíncronos a la lógica del negocio de la calculadora.

La estructura básica de la página.

El primer paso es crear la estructura básica de la página web que recibirá el usuario.  Esta se puede crear con cualquier editor de texto desde el bloc de notas hasta otro mas elaborado como Dreamweaver o Eclipse PDT.  Lo importante es que sea un editor de archivos planos.  En mi caso estoy probando gPHPEdit para Linux el cual se ve hasta ahora simple y conciso.  Para el caso de Windows un editor simple como el Notepad++ servirá.

Creamos entonces el archivo calculadora.html con el siguiente contenido.

<!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>
       <!-- Documento HTML con carácteres UTF8 -->
       <meta http-equiv="Content-type" content="text/html; charset=utf-8" />

       <title>Calculadora AJAX</title>

       <!-- Clases CSS internas -->

       <!-- Referencias a Javascripts externos -->

       <!-- Código Javascript interno -->
    </head>
    <body>
        <!-- Contenido del documento -->
    </body>
</html>

He incluído algunos <!– comentarios –> que nos servirán mas adelante para ubicar donde en el documento se deberán agregar segmentos de código adicionales.  Como se puede apreciar, esperamos ceñirnos al XHTML 1.1 transitional.

El formulario de la calculadora.

Este deberá contar con las siguientes partes básicas.

  1. Una sección para los mensajes de error (mensaje) de la aplicación.
  2. Un form (formulario) que representa al formulario en HTML.
  3. Un campo de texto (operando1) para 4 dígitos para que el usuario ingrese al primer operando.
  4. Un campo de selección (operador) para que el usuario elija la operación a efectuarse: suma, resta, multiplicación o división.
  5. Un campo de texto (operando2) para 4 dígitos para que el usuario ingrese al segundo operando.
  6. Un botón (igual) para invocar la operación de los datos.
  7. Una sección (resultado) para mostrar el resultado de la operación.

La siguiente es la implementación de esta sección y debe insertarse como el Contenido del documento en la plantilla de la estructura básica de la página web.

<div id='pagina'>
    <div id='mensaje'>&nbsp;</div>

    <form id='formulario' action='#' method='post'>
        <input type='text' id='operando1' name='operando1' value='' size='4' maxlength='4' />

        <select id='operador' name='operador'>
            <option value='SU'>+</option>
            <option value='RE'>-</option>
            <option value='MU'>*</option>
            <option value='DI'>/</option>
        </select>

        <input type='text' id='operando2' name='operando2' value='' size='4' maxlength='4' />

        <input type='button' id='igual' value='=' />

        <span id='resultado'>&nbsp;</span>
    </form>
 </div>
Nota.  El atributo del nombre (name) es requerido en los campos del formulario que se van a transmitir a través de la invocación asíncrona cuando el formulario es enviado completo como lo vamos a hacer en esta práctica.

Mejorar la presentación del formulario.

Este es el producto final de la actualización de la presentación de la página web, es necesario modificar los atributos visuales de cada una de las partes del formulario recién creado.

Interfaz de usuario de la calculadora
Interfaz de usuario de la calculadora

Las nuevas características se especifican utilizando hojas de estilo (CSS) y se referencian a través de sus identificadores, etiquetas o clases.

El siguiente código CSS realiza las modificaciones propuestas al formulario.  Estas se deben insertar en la sección de Clases CSS Internas.

<style type="text/css">

 #pagina                                       /* Envoltura general */
 {
       margin: auto;                           /* Margenes (centrado) */
       width: 600px;                           /* Ancho */
       text-align: center;                     /* Textos internos centrados */
       font-family: "arial, sans-serif";       /* Fuente del texto */
 }

 #mensaje                                      /* Mensaje de error */
 {
       width: 100%;                            /* Ancho */
       margin-top: 20px;                       /* Margen superior */
       margin-bottom: 30px;                    /* Margen inferior */
       color: #ff0e0e;                         /* Color del texto (foreground) */
       font-size: 12px;                        /* Tamaño de la fuente */
 }

 #operando1, #operando2                        /* Campos de texto de los operandos */
 {
       font-size: 25px;                        /* Tamaño de la fuente */
       border-style: groove;                   /* Estilo del borde */
 }

 #operador                                     /* Selector del operador */
 {
       font-size: 25px;                        /* Tamaño de la fuente */
 }

 #igual                                        /* Botón para realizar la operación */
 {
       font-size: 25px;                        /* Tamaño de la fuente */
 }

 #resultado                                    /* Campo de texto donde se despliega el resultado */
 {
       font-size: 25px;                        /* Tamaño de la fuente */
       font-style: italic;                     /* Atributo de fuente itálica o cursiva */
 }

 </style>

La lógica del negocio de la calculadora.

El hecho de calcular el valor resultante de la operación elegida a partir de los operandos especificados es lo que llamamos la lógica del negocio.  Esta lógica, como se dijo inicialmente, la vamos a implementar en PHP en el archivo calcular.php.

Los pasos generales que deben realizarse para llevar a cabo exitosamente el cálculo del valor resultante son los siguientes.

  1. Obtener la información provista por el usuario: operandos y operador.
  2. Verificar que la información proporcionada por el usuario sea válida.
  3. Realizar la operación indicada con los valores suministrados.
  4. Mostrar el resultado.

Como veremos a continuación la implementación de estos pasos es ligeramente mas interesante que su simple mención.

Para iniciar el código PHP, le indicamos al navaegador que la respuesta del script será un texto.  Este paso específico es opcional ya que para PHP este es el tipo de respuesta por defecto.

<?php

/* Indicar el tipo de contenido que tendrá la respuesta */

header('Content-type: text/html');

La implementación de las siguientes acciones la vamos a recubrir en un bloque try/catch ya que es suceptible de generar errores y consecuentemente, lanzar excepciones.

El siguiente paso es el obtener, intentar sanear y verificar los operandos proporcionados por el usuario desde el formulario HTML.  Los parámetros fueron transmitidos a través del método post y los elementos se diferencian por su identificador.

try
{
    /* Obtener y sanear los valores de los operadores */

    $operando1 = filter_var($_POST['operando1'], FILTER_SANITIZE_NUMBER_FLOAT);
    $operando2 = filter_var($_POST['operando2'], FILTER_SANITIZE_NUMBER_FLOAT);

    /* Verificar que los valores de los operadores correspondan
       con los tipos esperados */

    if(!filter_var($operando1, FILTER_VALIDATE_FLOAT) ||
       !filter_var($operando1, FILTER_VALIDATE_FLOAT))
           throw new Exception("Operandos inválidos: [{$operando1}] y [{$operando2}]");

Realizamos una tarea similar con el operador, obtenemos su valor y verificamos que sea válido.

    /* Obtener el valor del operador */

    $operador = $_POST['operador'];

    /* Verificar que el operador suministrado sea válido */

    if(!in_array($operador, array('SU', 'RE', 'MU', 'DI')))
        throw new Exception ("Operador inválido: [{$operador}]");

Definimos una variable para almacenar el resultado de la operación solicitada.

    /* Almacenar resultado, inicialmente desconocido */

    $resultado = 0;

Realizamos la operacion correspondiente al operador recibido.  Se deben tener en cuenta dos posibles contingencias que pueden suceder: en el caso de la division, el denominador, es decir, el segundo operador no puede ser cero.  El segundo caso que debe tenerse en cuenta es que el operador recibido no corresponda con ninguna de las operaciones conocidas, esto habria sido validado inicialmente al verificar el operador suministrado despues de su recuperacion.

    /* Realizar la operación solicitada */

    switch($operador)
    {
        case 'SU':    $resultado = $operando1 + $operando2;
        break;

        case 'RE':    $resultado = $operando1 - $operando2;
        break;

        case 'MU':    $resultado = $operando1 * $operando2;
        break;

        case 'DI':    /* Verificar si el denominador es cero,
                         en ese caso, la división no puede realizarse */

                      if ($operando2 == 0)
                          throw new Exception ('División por cero');

                      $resultado = $operando1 / $operando2;
        break;

        default:      /* Si ninguna operación se ejecutó significa
                         que el operador era inválido (segunda verificación) */

                      throw new Exception('Operador desconocido');
        break;
    }
}

Para terminar la lógica de la calculadora debemos lidiar con los dos posibles resultados.  Si el procesamiento tuvo algún error (se lanzó una excepción durante el proceso) o si su terminación fue exitosa.

En caso de haber sucedido un error este se indica al usuario mediante un mensaje descriptivo y al navegador al enviarle un código 400.

catch(Exception $e)            /* La operación produjo un error */
{
    /* Indica al navegador la condición de error */

    header("Status: 400 Bad Request", true, 400);

    /* Despliega el mensaje de error para el usuario */

    echo $e -> getMessage();

    exit(1);
}

En caso de terminar la operación exitosamente, se deberá mostrar al usuario el resultado de la misma e informarle al navegador con un código 200.

/* La operación se realizó exitosamente */

/* Indica al navegador la condición de éxito */

header("Status: 200 OK", true, 200);

/* Despliega el resultado de la operación para el usuario */

echo number_format($resultado, 4);

exit(0);

?>

Incluír la referencia a jQuery.

En este paso lo que hacemos en incluír a la librería jQuery en nuestro proyecto web para poder utilizarla en nuestros fragmentos de código Javascript.

Para hacer esto se debe agregar la siguiente línea en la sección Referencias de Javascript externos.

<script src="http://www.google.com/jsapi"></script>
<script>google.load("jquery", "1");</script>

Este fragmento utiliza Google AJAX Libraries que nos permite incluír los principales frameworks de Javascript en nuestros sitios web sin preocuparnos por su almacenamiento o implementación, en este caso, incluír la distribución estable mas reciente de la rama 1.x.  Obviamente este método requiere que el cliente tenga acceso a Internet además de la aplicación web.

Una forma mas tradicional de hacer lo mismo es el descargar por nuestra propia cuenta la distribución de jQuery desde su sitio web, almacenar el archivo en nuestro servidor y hacer una referencia desde la página web como se muestra a continuación.

<script src='http://www.servidor.com/ruta/jquery.js' type='text/javascript'></script>

Implementar las invocaciones asíncronas.

La implementación de la invocación asíncrona se realiza directamente en Javascript utilizando a la librería jQuery recién referenciada.  Su código se agrega bajo la sección Código Javascript interno.

El primer paso que se debe realizar para su implementación es relacionar el evento de presionar el botón igual con la invocación del llamado asíncrono.  Para hacer esto vamos a utilizar un método no invasivo, es decir, no vamos a contaminar el HTML con el atributo onClick para el elemento INPUT sino que lo haremos desde el código Javascript favoreciendo la separación entre presentación/lógica del negocio y maximizando la flexibilidad de esta última.

Para hacer esto, tan pronto como se carga la página se asocia el evento de clic sobre el botón de igual para que se invoque la función procesar.

<script type='text/javascript'>

/* Código a ejecutarse tan pronto como la
   página ha sido cargada por el navegador */

$(document).ready(function ()
{
	/* Asociar el evento de clic del botón 'igual'
	   con la lógica del negocio de la aplicación */

	$('#igual').click(function()
	{
		procesar();
	});
});

El siguiente paso es la implementación de la función procesar.  En ella se realiza el llamado asíncrono a través de AJAX a la aplicación web en PHP desarrollada anteriormente para realizar el cálculo solicitado.

Una invocación asíncrona típica incluye la siguiente información básica.

  1. El URL de la aplicación web a invocarse.
  2. El tipo del requerimiento a realizarse (GET o POST normalmente).
  3. Los datos a enviarse como parámetros desde el formulario web (en formato QueryString).
  4. Definir la implementación de que hacer si la invocación es exitosa.
  5. Definir la implementación de que hacer si la invocación es fallida.

En el siguiente fragmento de código se especifican los primeros tres elementos de la lista.  El llamado asíncrono accederá a la aplicación web calcular.php mediante el método post y se le enviarán como parámetros los campos del formulario formulario.

function procesar()
{
	$.ajax ({
		url: 	'calcular.php',                   /* URL a invocar asíncronamente */
		type:   'post',                           /* Método utilizado para el requerimiento */
		data: 	$('#formulario').serialize(),     /* Información local a enviarse con el requerimiento */

En caso de ser exitoso se deberá mostrará un mensaje verde informándolo y se desplegará el resultado obtenido de la operación en la ubicación definida para este fin.

		/* Que hacer en caso de ser exitoso el requerimiento */

		success: 	function(request, settings)
		{
			/* Cambiar el color del texto a verde */

			$('#mensaje').css('color', '#0ab53a');

			/* Mostrar un mensaje informando el éxito sucedido */

			$('#mensaje').html("Operación realizada exitosamente");

			/* Mostrar el resultado obtenido del cálculo solicitado */

			$('#resultado').html(request);
		},

En caso de que la invocación sea fallida se deberá mostrar un mensaje rojo informando el error sucedido y se removerá cualquier resultado previo que pueda haber para no confundir al usuario con información anterior.

		/* Que hacer en caso de que sea fallido el requerimiento */

		error: 	function(request, settings)
		{
			/* Cambiar el color del texto a rojo */

			$('#mensaje').css('color', '#ff0e0e');

			/* Mostrar el mensaje de error */

			$('#mensaje').html('Error: ' + request.responseText);

			/* Limpiar cualquier resultado anterior */

			$('#resultado').html('Error');
		}
	});  // Fin de la invocación al método ajax
}  // Fin de la función procesar

</script>

Enlaces.

id='operador'

Hacer algo cuando inicia o termina el evento AJAX con jQuery

Introducción.

Este pequeño fragmento de código es muy útil, permite manipular un objeto del DOM cuando suceden los eventos de inicio o terminación del AJAX.  Muy útil para mostrar de manera fácil y automatizada un indicador de AJAX para informar al usuario que hay una transacción asíncrona en curso.

Procedimiento.

$('#loader').ajaxStart(function () {
    $(this).fadeIn();
});

$('#loader').ajaxStop(function () {
    $(this).fadeOut();
});

Adicionalmente hay otros eventos que pueden manejarse de igual manera ajaxComplete, ajaxError, ajaxSend y ajaxSuccess, además de los ya mencionados ajaxStart y ajaxStop.

Enlaces.

Introducción al AJAX con Mootools 1.2.1

Introducción.

De vuelta a Mootools, fue el primero de los frameworks de Javascript que utilicé hace un poco mas de un año.  Después he experimentado un poco mas con Prototype/Scriptaculous y ahora estoy empezando a leer un poco acerca de jQuery.  Sin embargo siempre me ha gustado Mootools y es hora de documentar, tal y como se hizo con Prototype, como se realizan los llamados asíncronos (AJAX) con él.

En este artículo voy a describir las cuatro maneras como es posible realizar llamados con AJAX utilizando MooTools.

  1. El método load de los elementos.
  2. El método Request.HTML.
  3. El objeto Request directamente.
  4. El método Request.JSON.

1. El método load de los elementos.

Este método lo tienen los elementos (componentes) de Mootools.  Es muy útil porque permite actualizar rápidamente cualquier elemento con el contenido HTML retornado por un llamado asíncrono.  A pesar de ser muy fácil de utilizar, carece de las opciones y la flexibilidad de los métodos siguientes, por ejemplo, del paso de parámetros.

Para hacer uso de este método se utiliza la siguiente instrucción.

$('componente').load(urlRemota);

Donde componente es el elemento que se desea actualizar (un div por ejemplo) y urlRemota es el URL que se invoca de manera asíncrona para obtener el código HTML con el que se reemplazará el contenido del elemento.

Para más información acerca de este método consultar la documentación de Request.HTML.

2. El método Request.HTML.

Este método es una facilidad implementada sobre el método analizado a continuación.  Permite obtener el mismo objetivo del método anterior con una mayor flexibilidad.

    var myHTMLRequest = new Request
        .HTML({
            url:       urlRemota,
            update:    'componente',
            method:    'post',
            data:      $('formulario'),

            onRequest: function()
            {
                // Al iniciar la solicitud.
            },

            onComplete: function(html)
            {
                // Al completarse la solicitud.
            },

            onSuccess: function(responseTree, responseElements, responseHTML, responseJavaScript)
            {
                // Si la solicitud se completó exitosamente.

                /*
                   responseTree - (element) The node list of the remote response.
                   responseElements - (array) An array containing all elements of the remote response.
                   responseHTML - (string) The content of the remote response.
                   responseJavaScript - (string) The portion of JavaScript from the remote response.
                */
            },

            onFailure: function(elemento)
            {
                // Si la solicitud se completó con problemas.
            }
        })
        .send();

De manera similar al método anterior, el atributo url define el URL que se invocará para obtener de manera asíncrona el código HTML para actualizar el elemento referenciado por el atributo update.  El atributo method define si se utilizará POST o GET para la invocación del llamado.

El resto de atributos son opcionales.   El atributo data permite especificar variables que van a ser incluídas con el requerimiento.  Pueden especificarse como un query string de la forma “var1=valor1&var2=valor2”, como un hash de la forma {“var1”: “valor1”, “var2”: “valor2”} o enviando un formulario por completo de la forma $(‘id_formulario’).

Los atributos onXXX son manejadores de eventos a manera de callbacks y son ejecutados según lo que acontezca durante la solicitud del llamado asíncrono.

  • onRequest: ha sido iniciado el proceso de la solicitud.
  • onComplete: la solicitud ha terminado.
  • onSuccess: la solicitud ya ha terminado y terminó exitosamente.
  • onFailure: la solicitud ya ha terminado y terminó de manera fallida.

Para más información acerca de este método consultar la documentación de Request.HTML.

3. El objeto Request directamente.

Esta es la forma mas flexible de utilizar AJAX con MooTools ya que permite realizar la solicitud y manipular que se desea hacer con el resultado obtenido, ya no necesariamente actualizar un elemento.

    var myRequest = new Request({
        url:    urlRemota,
        method: 'post',
        data:   $('formulario'),

        onRequest: function()
        {
            // Al iniciar la solicitud.
        },

        onComplete: function(html)
        {
            // Al completarse la solicitud.
        },

        onSuccess: function(responseText, responseXML)
        {
            // Si la solicitud se terminó exitosamente.

            /*
                responseText - (string) The returned text from the request.
                responseXML - (mixed) The response XML from the request.
            */

            // Aquí se implementa que hacer con la respuesta del llamado asíncrono
            // cuando el procedimiento fue exitoso.

            /*
                Actualizando manualmente el componente,
                reproduce la funcionalidad de Request.HTML:

                $('componente').set('html', responseText);
            */
        },

        onFailure: function(xhr)
        {
            // Si la solicitud se completó con problemas.

            /*
                xhr - (XMLHttpRequest) The transport instance.
            */

            // Aquí se implementa que hacer con la respuesta del llamado asíncrono
            // cuando el procedimiento fue fallido.
        },

        onCancel: function()
        {
            // La solicitud fue cancelada.
        },

        onException: function(headerName, value)
        {
            // La transmisión de la cabecerá falló.

            /*
                headerName - (string) The name of the failing header.
                value - (string) The value of the failing header.
            */
        }
    }).send();

Se incluyen dos manejadores de eventos adicionales.

  • onCancel: la solicitud ha sido cancelada.
  • onException: la transmisión de la cabecera (header) provocó una excepción.

Se incluyen además, entre otras cosas, la propiedad running que permite verificar si el objeto myRequest se encuentra o no actualmente procesando una solicitud y al método cancel() que permite cancelar la solicitud activa, generando consecuentemente un evento de cancelación (ver onCancel).

Para más información acerca de este método consultar la documentación de Request.

4. El método Request.JSON.

Es una facilidad construída sobre el objeto Request que permite manipular respuestas de llamadas asíncronas en formato JSON (Javascript Object Notation).

    var jsonRequest = new Request.JSON({
        url:    urlRemota,
        method: 'post',
        data:   $('formulario'),

        onRequest: function()
        {
            // Al iniciar la solicitud.
        },

        onComplete: function(responseJSON)
        {
            // Al completarse la solicitud.
        },

        onSuccess: function(responseJSON, responseText)
        {
            // Si la solicitud se terminó exitosamente.

            /*
                responseJSON - (object) The JSON response object from the remote request.
                responseText - (string) The JSON response as string.
            */

            /*
                La información recibida del llamado asíncrono se puede obtener como atributos
                del objeto responseJSON, por ejemplo acceder a responseJSON.nombres
            */
        },

        onFailure: function(xhr)
        {
            // Si la solicitud se completó con problemas.

            /*
                xhr - (XMLHttpRequest) The transport instance.
            */
        }
    }).send();

Para más información acerca de este método consultar la documentación de Request.JSON.

Ejemplo funcional.

La aplicación de ejemplo obtiene información dinámica del servidor de manera asíncrona, para actualizar los campos DIV de la página utilizando cada una de las cuatro técnicas descritas en este artículo.

Para la generación de la información dinámica del lado del servidor se utilizaron dos scripts muy simples desarrollado en PHP, uno de ellos genera una cadena constante junto con la fecha actual y el otro prepara la respuesta JSON a partir de la información recibida.  Además muestra la información recibida a través del POST.  El script ContenidoRemoto.php es utilizado para los métodos 1, 2 y 3 mientras que el script Contenido RemotoJson.php es utilizado para el método 4.

Para consultar el ejemplo funcionando en el servidor de demostración se debe seguir este enlace.

Para consultar el código fuente de cada uno de los archivos que componen el ejemplo se deben seguir los enlaces dispuestos a continuación: index.php, ContenidoRemoto.php y ContenidoRemotoJson.php.

Enlaces.

Resumen de AJAX con Prototype

Introducción.

Para finalizar la serie de artículos que había escrito acerca del uso de AJAX con el framework Prototype voy a realizar un muy breve resúmen de la sintaxis de las tres formas de realizar la invocación que me serán útiles para futuras referencias rápidas.

Utilizando el Updater.

El Ajax.Updater es una facilidad que toma el contenido del resultado del llamado asíncrono y inserta en el interior del componente especificado.

new Ajax.Updater(ID_COMPONENTE,
                 URL,
{
    parameters: $('FORMULARIO').serialize(true),
    method: 'post'
});

Utilizando el Request.

Esta opción es mas flexible que la anterior y permite manipular el contenido de la respuesta del llamado asíncrono.

new Ajax.Request(URL,
{
    parameters: $('FORMULARIO').serialize(true),
    method: 'post',
    onSuccess: function(transport)
    {
        // Hacer algo en éxito.
    },
    onFailure: function(transport)
    {
        // Hacer algo en fracaso.
    },
    onComplete: function(transport)
    {
        // Hacer algo al terminar.
    }
});

Utilizando JSON para el transporte.

El procedimiento es similar al anterior.  La diferencia es que la respuesta del llamado asíncrono viene empaquetada en un objeto JSON, diferente a la invocación anterior en la que se recibe como una cadena de texto.

new Ajax.Request(URL,
{
    parameters: $('FORMULARIO').serialize(true),
    method: 'post',
    requestHeaders:
    {
        Accept: 'application/json'
    },
    onSuccess: function(transport)
    {
        // Hacer algo en éxito.
        var json = transport.responseText.evalJSON(true);
        // var valorX = json.x;
    },
    onFailure: function(transport)
    {
        // Hacer algo en fracaso.
    },
    onComplete: function(transport)
    {
        // Hacer algo al terminar.
    }
});

Del lado del servidor, en PHP, el resultado final se debe encapsular en un objeto JSON antes de enviarlo al cliente.

echo "/*-secure-n";
echo json_encode($contacto -> toArray());
echo "n*/";
header('Content-type: application/json');
header("Status: 200 OK", false, 200);

En caso de error se debe retornar, al igual que en los demás casos, un mensaje cuyo tipo sea != 200.

header("Status: 400 Bad request", false, 400);

Ejemplo 2 de Ajax con Prototype y PHP

Mi interés en esta miniserie de artículos es la enseñar de manera fácil y didáctica las bases prácticas del desarrollo de aplicaciones web basadas en Javascript Asíncrono (o AJAX).  Para esto se utilizará la librería Prototype que facilitará la implementación de Javascript en el lado del cliente y PHP como lenguaje dinámico en el  lado del servidor, accediendo a una base de datos SQLite que se utilizará como capa de persistencia.

La serie de artículos se divide según cada una de las etapas del desarrollo de la aplicación de ejemplo, la cual consiste en un administrador de contactos muy simple.  Los capitulos mencionados a continuación comprenden la implementación del código XHTML, Javascript general, la creación de la base de datos, el código PHP para acceder a la base de datos y el acceso a la información mediante AJAX.

  1. La interfaz del usuario.
  2. Preparando el escenario con JavaScript.
  3. Estableciendo la persistencia.
  4. Creando el modelo de datos.
  5. Implementando las acciones con AJAX (I).
  6. Implementando las acciones con AJAX (II).

Cada uno de los artículos divide el código de la implementación del subtema que le compete para ser comentado paso a paso.  En el caso en que se prefiera dar un vistazo al código consolidado del capítulo se puede consultar al final de los artículos el producto final de la implementación allí realizada, así como descargar el código fuente de la aplicación implementada hasta el momento.

Espero que esta miniserie les sea de interés y utilidad, y que les permita conocer el desarrollo de aplicaciones web asíncronas mediante el uso de las herramientas elegidas.

Enlaces.

Ejemplo 2 de Ajax con Prototype: implementando las acciones con AJAX (II)

Introducción.

En este capítulo final se va a realizar la implementación de las acciones que permanecen pendientes.

  • Editar a un contacto.
  • Remover a un contacto.

Estas acciones requieren que se haya realizado con anterioridad la implementación de la lista y la carga de contactos para que el usuario tenga la posibilidad de ver los contactos existentes en la base de datos y de seleccionar uno específico sobre el cual se va a ejecutar la acción elegida.

Estas acciones estan basadas en Ajax.Request motivo por el cual su implementación es muy similar a la acción de agregar contactos y carecen de aspectos particulares para destacar.

Editando contactos.

En el lado del cliente.

Se actualiza la implementación de la función JavaScript enEditarRegistro en la cual se realizan las siguientes actividades.

  • Verificar que el contenido del campo oculto que almacena el ID del contacto que se va a editar no se encuentre vacío.
    if($('tId').value.strip().length == 0)
    {
        alert('Para editar un registro primero debe seleccionarlo.');

        return false;
    }
  • Realizar la invocación del llamado asíncrono con las siguientes características.
  • Como parámetros se envían los campos del formulario.
  • Si la invocación asíncrona termina exitosamente se muestra un mensaje y se actualiza el listado de contactos registrados para que se reflejen los cambios.
  • Si la invocación termina de manera fallida se muestra un mensaje indicando el error.
    new Ajax.Request('editar.php',
    {
        method:     'post',

        parameters: $('formulario').serialize(true),

        onSuccess: function(transport)
        {
            ponMensajeBien('<b>' + transport.responseText + '</b>');

            enActualizarBusqueda(null);
        },

        onFailure: function(transport)
        {
            ponMensajeMal('<b>' + transport.responseText + '</b>');
        }
    });

    return true;

En el lado del servidor.

La implementación se realiza en el archivo editar.php que se ubica en el directorio raíz de la aplicación web en el cual se desarrollan las siguientes actividades.

  • Establecer la conexión con la base de datos.
include('config.php');
include_once('basededatos.php');
include_once('contacto.php');

$bd = new BaseDeDatos(BaseDeDatos::crearDSN($bdInfo));
  • Obtener la nueva información del contacto enviada por el cliente.
$tId          = filter_var($_POST['tId'],          FILTER_SANITIZE_STRING);
$tNombres     = filter_var($_POST['tNombres'],     FILTER_SANITIZE_STRING);
$tApellidos   = filter_var($_POST['tApellidos'],   FILTER_SANITIZE_STRING);
$tCorreo      = filter_var($_POST['tCorreo'],      FILTER_SANITIZE_EMAIL);
$tFNacimiento = filter_var($_POST['tFNacimiento'], FILTER_SANITIZE_STRING);
  • Crear una instancia de la clase Contacto con la información recién recibida y realizar la actualización del contacto a través de ella.
$contacto = new Contacto();

$contacto -> ponId($tId);
$contacto -> ponNombres($tNombres);
$contacto -> ponApellidos($tApellidos);
$contacto -> ponCorreo($tCorreo);
$contacto -> ponFechaDeNacimiento($tFNacimiento);

$control = $contacto -> editar($bd);

$bd -> desconectar();
  • Enviar al cliente el mensaje informativo y el código HTTP apropiado según el tipo de terminación del proceso.
if($control > 0)
{
    echo utf8_encode("Registro editado");

    header("Status: 200 OK", false, 200);
}
else
{
    echo utf8_encode("El registro no pudo ser editado, inténtelo nuevamente");

    header("Status: 400 Bad request", false, 400);
}

Removiendo contactos.

Nuevamente el procedimiento es similar al anterior, se utiliza Ajax.Request.

En el lado del cliente.

  • Verificar que el contenido del campo oculto que almacena el ID del contacto que se va a remover no se encuentre vacío.
    if($('tId').value.strip().length == 0)
    {
        alert('Para remover un registro primero debe seleccionarlo.');

        return false;
    }
  • Realizar la invocación asíncrona de la acción consultando al archivo remover.php.
    new Ajax.Request('remover.php',
    {
        method:     'post',

        parameters: $('formulario').serialize(true),

        onSuccess:  function(transport)
        {
            ponMensajeBien('<b>' + transport.responseText + '</b>');

            enActualizarBusqueda(null);
        },

        onFailure:  function(transport)
        {
            ponMensajeMal('<b>' + transport.responseText + '</b>');
        }
    });

    return true;

En el lado del servidor.

  • Establecer la conexión con la base de datos.
include('config.php');
include_once('basededatos.php');
include_once('contacto.php');

$bd = new BaseDeDatos(BaseDeDatos::crearDSN($bdInfo));
  • Obtener la información del contacto enviada por el cliente, es de particular interés la información del campo tId.
$tId = filter_var($_POST['tId'], FILTER_SANITIZE_STRING);
  • Crear una instancia de la clase Contacto con la información recién recibida y realizar la remoción del contacto a través de ella.
$contacto = new Contacto();

$contacto -> ponId($tId);

$control = $contacto -> remover($bd);

$bd -> desconectar();
  • Enviar al cliente el mensaje informativo y el código HTTP apropiado según el tipo de terminación del proceso.
if($control > 0)
{
    echo utf8_encode("Registro removido");

    header("Status: 200 OK", false, 200);
}
else
{
    echo utf8_encode("El registro no pudo ser removido, inténtelo nuevamente");

    header("Status: 400 Bad request", false, 400);
}

Conclusiones.

En los últimos dos capítulos se realiza la implementación de los llamados asíncronos desde la interfaz del usuario (navegador web) utilizando JavaScript que son respondidos por las acciones implementadas en PHP.

Las implementaciones de Ajax hace uso de sus tres presentaciones:

  1. Ajax.Request: útil para realizar el llamado asíncrono, recibir la información de respuesta y realizar cierto grado de procesamiento sobre ella.  Utilizado para agregar, editar y remover contactos.
  2. Ajax.Updater: es una facilidad basada en el tipo anterior.  Su utilidad radica en que el mismo framework se encarga de desplegar la información recibida como respuesta del servidor en el componente especificado de manera automática.  Utilizado para mostrar el listado de los contactos registrados en el sistema.
  3. Ajax.Request con JSON: el involucrar JSON como formato para la respuesta recibida del servidor le permite al cliente realizar fácilmente cualquier procesamiento o transformación de esta información ya que es inmediatamente convertida a un objeto JavaScript con todas las ventajas que esto proporciona.  Utilizado para mostrar la información (cargar) del contacto seleccionado por el usuario.

Enlaces.

Ejemplo 2 de Ajax con Prototype: implementando las acciones con AJAX (I)

Introducción.

Para terminar este mini-tutorial se deben implementar las acciones que corresponden con los requerimientos pendientes de los capítulos anteriores.

La implementación de estas acciones se va a realizar del lado del servidor utilizando AJAX mediante Prototype y PHP y la infraestraestructura recién creada del lado del servidor.  Respecto a AJAX se van a implementar las tres modalidades que permite Prototype.

  1. Ajax.Request.  Realiza la solicitud asíncrona y recibe la respuesta del servidor permitendo manipularla y procesarla según se requiera.  Es posible determinar el éxito o fracaso de la comunicación con el servidor.
  2. Ajax.Updater.  Es un subcaso del método anterior.  Realiza el llamado asíncrono y actualiza automáticamente el contenido de un componente XHTML con la respuesta obtenida del servidor.
  3. Ajax.Request con JSON.  Realiza la solicitud asíncrona y recibe la respuesta del servidor en formato JSON facilitando su manipulación.

Si se reucerda las acciones que quedaron pendientes de implementación fueron las siguientes.

  • Agregar un registro.
  • Editar un registro.
  • Eliminar un registro.
  • Actualizar el listado de contactos.
  • Cargar un contacto específico.

La primera de las acciones que se va a implementar es la de agregar registros ya que le permitirá al usuario empezar a poblar la base de datos.  La implementación de cada acción incluirá modificaciones a los archivos del lado del cliente (JavaScript) y del lado del servidor (PHP).

Agregando registros.

En el lado del cliente.

Se debe actualizar el manejo del evento que sucede ante la solicitud del usuario de agregar un nuevo registro de un Contacto, para esto editamos el archivo jsfunciones.js y modificamos el contenido de la función enAgregarRegistro.

La función debe realizar las siguientes actividades.

  • Realizar las validaciones necesarias para garantizar que los datos se encuentran dispuestos para ejecutarse la acción solicitada.
    • Verificar que todos los campos tengan contenido, es decir, el contenido de ninguno de los campos puede ser vacío.
    var camposVacios = new Array();

    $('formulario').select('.CampoEstilo').each(function (elemento)
    {
        if(elemento.value.strip().length == 0)
        {
            camposVacios.push(elemento.title);
        }
    });

    if(camposVacios.length > 0)
    {
        alert('Los siguientes campos se encuentran vacíos,n' +
              'por favor complemente su información: nn' +
              camposVacios.toString());

        return false;
    }
  • Realizar el llamado asíncrono a la acción agregar.php para adicionar a la base de datos la información contenida en el formulario.
    • En caso de ser exitosa la ejecución de la acción se deberá mostrar en color verde (.MensajeBien) la respuesta obtenida del servidor y actualizar el listado de los clientes registrados donde se deberá reflejar la adición del nuevo registro.
    • En caso de ser fallida la ejecución, se deberá mostrar en color rojo (.MensajeMal).
    new Ajax.Request('agregar.php',
    {
        method:     'post',

        parameters: $('formulario').serialize(true),

        onSuccess: function(transport)
        {
            ponMensajeBien('<b>' + transport.responseText + '</b>');

            enActualizarBusqueda(null);
        },

        onFailure: function(transport)
        {
            ponMensajeMal('<b>' + transport.responseText + '</b>');
        }
    });

    return true;

En el lado del servidor.

Se define la implementación en el archivo agregar.php en el directorio raíz de la aplicación web.

Las actividades realizadas por esta acción se relacionan a continuación.

  • Establecer la conexión con la base de datos.
include('config.php');
include_once('basededatos.php');
include_once('contacto.php');

$bd = new BaseDeDatos(BaseDeDatos::crearDSN($bdInfo));
  • Obtener la información enviada por el cliente.  Esta información viene a través del método POST y los nombres de las variables suministradas corresponden con los nombres de los campos del formulario.
$tNombres     = filter_var($_POST['tNombres'],     FILTER_SANITIZE_STRING);
$tApellidos   = filter_var($_POST['tApellidos'],   FILTER_SANITIZE_STRING);
$tCorreo      = filter_var($_POST['tCorreo'],      FILTER_SANITIZE_EMAIL);
$tFNacimiento = filter_var($_POST['tFNacimiento'], FILTER_SANITIZE_STRING);
  • A partir de la información recién obtenida se crea una instancia del objecto Contacto y almacena su información utilizando su método agregar.
$contacto = new Contacto();

$contacto -> ponNombres($tNombres);
$contacto -> ponApellidos($tApellidos);
$contacto -> ponCorreo($tCorreo);
$contacto -> ponFechaDeNacimiento($tFNacimiento);

$control = $contacto -> agregar($bd);

$bd -> desconectar();
  • Según el resultado de la operación (valor de $control) se define si la ejecución fue exitosa o fallida y se envía al cliente el mensaje y código HTTP apropiado.
if($control > 0)
{
    echo utf8_encode("Registro agregado");

    header("Status: 200 OK", false, 200);
}
else
{
    echo utf8_encode("El registro no pudo ser agregado");

    header("Status: 400 Bad request", false, 400);
}

Listando contactos registrados.

Esta acción permitirá que se verifique si efectivamente la adición de registros anterior se encuentra funcionando correctamente o no al desplegar en la interfaz (6) el listado de contactos existente en la base de datos de acuerdo con el filtro de búsqueda seleccionado.

En el lado del cliente.

La misión de esta acción es la de actualizar al div Resultados con la información de la base de datos de acuerdo con los criterios contenidos en los campos sLetra y sCampo.  Se utiliza el Ajax.Updater para este fin.

    new Ajax.Updater('Resultados',
             'listar.php',
             {
                parameters: $('sLetra').serialize(true) + "&" + $('sCampo').serialize(true),

                method: 'post'
             });

En el lado del servidor.

Se realiza la implementación del archivo listar.php en el cual desarrollan las siguientes actividades.

  • Establecer la conexión con la base de datos.
include('config.php');
include_once('basededatos.php');
include_once('contacto.php');

$bd = new BaseDeDatos(BaseDeDatos::crearDSN($bdInfo));
  • Obtener el valor del campo sCampo y verificar que sea válido, es decir, sea nom o ape.
$campos = array('nom' => 'nombres',
                'ape' => 'apellidos');

$sCampo = filter_var($_POST['sCampo'], FILTER_SANITIZE_STRING);

if(!isset($campos[$sCampo]))
{
    echo utf8_encode("<span class='MensajeMal'>Los parámetros de búsqueda especificados son inválidos.</span>");

    header("Status: 400 Bad request", false, 400);

    exit;
}
  • Obtener el valor del campo sLetra.
$campo = $campos[$sCampo];

$sLetra = filter_var($_POST['sLetra'], FILTER_SANITIZE_STRING);
  • Crear la condición de filtro de búsqueda y obtener el listado de los Contactos que cumplen con dicho criterio.
$letra1 = (($sLetra == 'todos' ) ? "" : strtoupper($sLetra[0])) . "%";
$letra2 = (($sLetra == 'todos' ) ? "" : strtolower($sLetra[0])) . "%";

$where = $campo . " LIKE '{$letra1}' OR " . $campo . " LIKE '{$letra2}'";

$listado = Contacto::listar($bd, $where);
  • Generar la presentación del listado (XHTML) para ser enviado hacia el cliente para que sea ubicado en el contenedor apropiado de la página web.
echo "<table><tbody>";

echo "<tr><td class='RegistroTitulo'>Apellidos</td>" .
     "<td class='RegistroTitulo'>Nombres</td>" .
     "<td class='RegistroTitulo'>Correo</td>" .
     "<td class='RegistroTitulo'>FNacimiento</td></tr>";

foreach($listado as $registro)
{
    $id          = $registro['id'];
    $nombres     = urldecode($registro['nombres']);
    $apellidos   = urldecode($registro['apellidos']);
    $correo      = urldecode($registro['correo']);
    $fnacimiento = urldecode($registro['fnacimiento']);

    $e1 = "<a href='#' onClick='enCargarRegistro({$id}); return false;'>";
    $e2 = "</a>";

    echo "<tr><td class='RegistroListado'>{$e1}{$apellidos}{$e2}</td>" .
         "<td class='RegistroListado'>{$e1}{$nombres}{$e2}</td>" .
         "<td class='RegistroListado'>{$e1}{$correo}{$e2}</td>" .
         "<td class='RegistroListado'>{$e1}{$fnacimiento}{$e2}</td></tr>";
}

echo "</tbody></table>";

$bd -> desconectar();

Cargando registros.

El usuario puede seleccionar a uno de los Contactos registrados haciendo clic sobre sus datos en el componente (6), esta acción invoca a la función enCargarRegistro que conociendo el ID del Contacto debe obtener toda su información para ser desplegada en el formulario apropiado.

Esta acción requiere poder manipular de manera discreta e individual a la información retornada por el servidor ya que cada valor corresponderá con un campo diferente del Contacto seleccionado, lo cual es diferente a por ejemplo la acción anterior, la obtención del listado de Contactos, en la cual todo lo obtenido del servidor (XHTML) es transferido directamente y sin procesamiento a un div específico; por este motivo se involucrará el uso de JSON en la respuesta del llamado asíncrono.

En el lado del cliente.

  • La función enCargarRegistro recibe el ID del Contacto a obtener el cual se convierte en parámetro para la acción recuperar.php.
    new Ajax.Request('recuperar.php',
    {
        parameters: 'id=' + id,

        method: 'post',
  • Se especifica que el resultado de esta acción deberá ser del tipo Application/JSON.
        requestHeaders:
        {
            Accept: 'application/json'
        },
  • En caso de fracasar el llamado asíncrono se mostrará un mensaje indicándolo.
        onFailure: function(transport)
        {
            ponMensajeMal('<b>No fue posible recuperar el registro ' + id + '</b>: ' + transport.responseText);
        }
  • En caso de tener éxito el llamado asíncrono se ejecutarán las siguientes actividades.
  • Se evalúa la respuesta del servidor como un objeto JSON convirtiéndo la respuesta en un objeto JavaScript.
            var json = transport.responseText.evalJSON(true);
  • Se llenan los camps del formulario con la información recibida del servidor.
            $('tId').value          = json.id;
            $('tNombres').value     = json.nombres;
            $('tApellidos').value   = json.apellidos;
            $('tCorreo').value      = json.correo;
            $('tFNacimiento').value = json.fechaNacimiento;
  • Se modifica la acción del formulario a Editar ya que se cuenta con un registro seleccionado.
            $('bAccion').value = 'Editar';
  • Se muestra un mensaje de éxito.
            ponMensajeBien('<b>Registro recuperado: ' + json.id + '</b>');

En el lado del servidor.

La lógica de la acción se implementa en el archivo recuperar.php ubicado en la raíz de la aplicación web y en ella se desarrollan las siguientes actividades.

  • Establecer la conexión con la base de datos.
include('config.php');
include_once('basededatos.php');
include_once('contacto.php');

$bd = new BaseDeDatos(BaseDeDatos::crearDSN($bdInfo));
  • Obtener el identificador del Contacto enviado desde el cliente.
$tId = filter_var($_POST['id'], FILTER_SANITIZE_STRING);
  • Crear una instancia de la clase Contacto y a través de ella recuperar la información del contacto especificado por el usuario.
$contacto = new Contacto();

$control = $contacto -> recuperar($bd, $tId);

$bd -> desconectar();
  • A partir de la respuesta obtenida ($control) determinar si la acción fue realizada exitosa o fallidamente y realizar los procedimientos apropiados para informárselo al cliente.
  • En caso de éxito:
    • Enviar al cliente el objeto resultante en formato JSON.
    • Informar al cliente que el tipo de contenido enviado es Application/JSON.
    • Enviar el código HTTP de éxito.
if($control === true)
{
    echo "/*-secure-n";

    echo json_encode($contacto -> toArray());

    echo "n*/";

    header('Content-type: application/json');

    header("Status: 200 OK", false, 200);
}
  • En caso de fracaso:
    • Enviar al usuario el motivo del error con un mensaje informativo.
    • Enviar el código HTTP de fracaso.
else
{
    echo utf8_encode("El registro no pudo ser agregado");

    header("Status: 400 Bad request", false, 400);
}

Ejemplo 2 de Ajax con Prototype: creando el modelo de datos

Introducción.

Teniendo lista la capa de acceso a la base de datos se procede entonces a la creación del modelo de datos de la única estructura de persistencia que tiene el proyecto: la tabla Contacto. La utilidad de este modelo es la de abstraer del resto de la aplicación todo lo concerniente a su lógica especifica de negocio, es decir, especificar todo lo relacionado con su conducta, consulta y persistencia para que el resto de la aplicación se pueda desentender de ella y se modularice al estilo DRY

Creación de la clase contacto.

Para hacer esto se crea el archivo contacto.php en el directorio raíz del proyecto.

Los campos de la clase coinciden con los especificados en la tabla de la base de datos con igual nombre. La versión inicial de la clase se describe a continuación.

<?php

class Contacto
{
    private $id;
    private $nombres;
    private $apellidos;
    private $correo;
    private $fechaNacimiento;

    // Resto de la implementación.
}

?>

Se crean los métodos para modificar desde el exterior a los atributos de la instancia: métodos pon o setters.

    public function ponId($valor)
    {
        $this -> id = $valor;
    }

    public function ponNombres($valor)
    {
        $this -> nombres = $valor;
    }

    public function ponApellidos($valor)
    {
        $this -> apellidos = $valor;
    }

    public function ponCorreo($valor)
    {
        $this -> correo = $valor;
    }

    public function ponFechaDeNacimiento($valor)
    {
        $this -> fechaNacimiento = $valor;
    }

De igual manera se crean los métodos que permiten obtener el valor de los atributos de la instancia: métodos obt o getters.

    public function obtId()
    {
        return $this -> id;
    }

    public function obtNombres()
    {
        return $this -> nombres;
    }

    public function obtApellidos()
    {
        return $this -> apellidos;
    }

    public function obtCorreo()
    {
        return $this -> correo;
    }

    public function obtFechaDeNacimiento()
    {
        return $this -> fechaNacimiento;
    }

Estos métodos son el lugar idóneo para establecer restricciones en el dominio de los valores de los atributos como por ejemplo, evitar que al campo $correo se le asignen direcciones que no cumplan con el formato o que al campo $fechaNacimiento se le asignen fechas futuras entre otros. Por simplicidad de la aplicación de demostración este tipo de validaciones se omiten y se dejan libres para ser desarrolladas por el lector.

Implementación del acceso a la base de datos.

Como se mencionó inicialmente, el acceso a la base de datos no se realiza directamente utilizando las función de PDO provistas por PHP sino que por el contrario, se aprovecha la capa adicional de alto nivel implementada en el capítulo pasado.

Todas las operaciones descritas a continuación hacen referencia a como almacenar, editar, remover y recuperar Contactos (operaciones CRUD) a partir de la información de la instancia de la clase del mismo nombre.

Agregando contactos.

    public function agregar($bd)
    {
        $sql = "INSERT INTO contacto ('nombres', 'apellidos', 'correo', 'fnacimiento') " .
               "VALUES ('{$this -> nombres}', '{$this -> apellidos}', '{$this -> correo}', '{$this -> fechaNacimiento}')";

        return $bd -> ejecutar($sql);
    }

Editando contactos.

    public function editar($bd)
    {
        $sql = "UPDATE contacto SET nombres='{$this -> nombres}', " .
               "apellidos='{$this -> apellidos}', correo='{$this -> correo}', " .
               "fnacimiento='{$this -> fechaNacimiento}' WHERE id='{$this -> id}'";

        return $bd -> ejecutar($sql);
    }

Removiendo contactos.

    public function remover($bd)
    {
        $sql = "DELETE FROM contacto WHERE id='{$this -> id}'";

        return $bd -> ejecutar($sql);
    }

Recuperando contactos.

    public function recuperar($bd, $id)
    {
        $sql = "SELECT id, nombres, apellidos, correo, fnacimiento FROM contacto WHERE id='{$id}'";

        $res = $bd -> consultar($sql) -> fetch();

         if($res === false)
            return false;

        $this -> id              = $res['id'];
        $this -> nombres         = $res['nombres'];
        $this -> apellidos       = $res['apellidos'];
        $this -> correo          = $res['correo'];
        $this -> fechaNacimiento = $res['fnacimiento'];

        return true;
    }

Nótese como todos los métodos del modelo Contacto reciben por parámetro a la instancia de BaseDeDatos a la cual se va a acceder, esto quiere decir que es responsabilidad de la acción que los invoca el establecer la conexión y el validar que esta se haya realizado exitosamente.

También se puede observar que el método recuperar recibe un parámetro adicional al resto de los métodos el cual hace referencia al ID del Contacto que se desea recuperar, con él se realiza la consulta a la base de datos y en éxito se actualizan los campos de la instancia con la información obtenida. El resto de los métodos realizan su cometido: agregar, actualizar y borrar, a partir la información encontrada en sus atributos.

Listando contactos.

Un método adicional que requiere del acceso a la base de datos es el que permite obtener un listado de los contactos registrados filtrado de acuerdo a una serie de condiciones especificadas en ejecución. Este método será útil para implementar el listado de contactos con filtro de búsqueda (5 y 6).

	public static function listar($bd, $where=false)
	{
		$sql = "SELECT id, nombres, apellidos, correo, fnacimiento " .
	               "FROM contacto " .
		       (($where !== false) ? "WHERE " . $where . " " : "") .
		       "ORDER BY apellidos, nombres";

		return $bd -> consultar($sql);
	}

Métodos complementarios.

Un último método que se requiere como complemento para facilitar la manipulación del objeto utilizando JSON es el método toArray que convierte los atributos de la instancia a su correspondiente representación de arreglo, algo muy similar a lo realizado por la función get_object_vars.

    public function toArray()
    {
        return array('id'              => $this -> id,
                     'nombres'         => urldecode($this -> nombres),
                     'apellidos'       => urldecode($this -> apellidos),
                     'correo'          => urldecode($this -> correo),
                     'fechaNacimiento' => urldecode($this -> fechaNacimiento));
    }

Conclusiones.

Con la implementación del modelo que recubre a un mayor nivel las operaciones relacionadas con el acceso a la persistencia de los datos y la lógica del negocio se terminan los preparativos de infraestructura para la implementación de las acciones faltantes de la aplicación de ejemplo a través de accesos asíncronos.

Enlaces.