Realiza llamadas de servicio Ajax con PHP, jQuery y JSON

Un sistema centralizado y estandarizado basado en eventos

En este artículo, conozca un sistema para hacer y responder a las llamadas de servicio de Asynchronous JavaScript and XML (Ajax) de una manera coherente, basada en eventos. El sistema puede determinar si una llamada de proceso remoto tiene éxito o fracasa. Descubra cómo estandarizar el formato del resultado de los objetos retornados por las llamadas de servicio Ajax, proporcionar respuestas basadas en eventos a las llamadas de Ajax y centralizar el manejo de los resultados de Ajax. El sistema utiliza tecnologías PHP, jQuery y JSON, y un código de ejemplo lo guía a través de la construcción del sistema. El artículo finaliza con un ejemplo de llamada Ajax que muestra cómo interactúan las piezas del sistema.

Jeremy J. Wischusen, Web Application Architect, Binary Neuron L.L.C.

Jeremy Wischusen photoJeremy Wischusen tiene más de 13 años de experiencia diseñando sitios web y aplicaciones para clientes como Purple Communications, myYearbook.com, HBO y otros, desarrollando sistemas frontend y backend con Flash, Flex, jQuery, PHP, MySQL, MSSQL y PostgreSQL. Ha enseñado diseño web, Flash y ActionScript para clientes como Wyeth Pharmaceuticals, The Vanguard Group, Bucks County Community College y The University of the Arts.



25-09-2012

Introducción

En este artículo, conozca un sistema para hacer y responder a las llamadas de servicio Ajax de una manera coherente, basada en eventos utilizando PHP, jQuery y JSON. El sistema se destina a llamadas de Ajax para servicios como inicio de sesión y actualización de perfil (no para la simple carga de contenido, como una página HTML). Usted puede utilizar este sistema para determinar si una llamada de proceso remoto tiene éxito o fracasa.

Acrónimos de uso frecuente

  • Ajax: Asynchronous JavaScript + XML
  • DOM: Document Object Model
  • HTML: Hypertext Markup Language
  • JSON: JavaScript Serialized Object Notation
  • PHP: Hypertext Preprocessor

Para seguir con este artículo se supone que usted:

  • Está familiarizado con la base de la programación orientada a objetos en JavaScript y PHP.
  • Está familiarizado con el modelo de eventos DOM Level 2 y en cómo interactuar con ella en JavaScript. Para el sistema de ejemplo, usted interactúa con este modelo utilizando la librería jQuery.
  • Tiene un conocimiento básico de los conceptos de Ajax.
  • Sabe cómo los objetos se construyen y son referidos utilizando la notación JSON.

Consulte Resources para información adicional sobre estas tecnologías y conceptos.

Tecnologías subyacentes

El sistema descrito en este artículo utiliza las siguientes tecnologías:

  • Acceso a un servidor PHP 5 con una función json_encode de PHP (requiere PHP 5.2.0 o mayor y PHP Extension Community Library o PECL, JSON 1.2.0 o mayor).
  • La biblioteca jQuery JavaScript (Versión 1.4.4 o mayor).

Objetivos del sistema

El escenario involucra a una empresa que ha decidido rehacer uno de sus sitios web. Como parte del rediseño, la empresa quería que el inicio de sesión y la edición de perfiles se hiciera con ventanas emergentes Ajax. Debido a que el sitio ya estaba en renovación, se aprovechó la oportunidad para implementar ideas para estandarizar la manera de hacer y responder las llamadas de servicio Ajax.

El nuevo sistema fue creado para lograr tres objetivos principales:

  • Estandarizar el formato del resultado de los objetos retornados por las llamadas de servicio Ajax.
  • Proporcionar respuestas basadas en eventos a las llamadas de Ajax.
  • Centralizar el manejo de resultados de Ajax.

El resto de este artículo describe cómo el sistema alcanza estos objetivos.


Estandarizar el formato del resultado de los objetos retornados por las llamadas de Ajax.

La clase PHP ServiceResult estandariza el objeto resultante creado al hacer una llamada de Ajax. Todas las llamadas de servicio Ajax retornan un objeto codificado en JSON de este tipo, dando a todas las llamadas de servicio Ajax una interfaz coherente y predecible.

El Listado 1 muestra la clase PHP ServiceResult .

Listado 1. Clase PHP ServiceResult .
<?php class ServiceResult {

        /**
        *
        * @var Boolean
        */ 
        public $success;
        /**
        *
        * @var Array
        */
        public $errors;
        /**
        *
        * @var Array
        */ 
        public $messages;
        /**
        * Holds data returned by a service or passthrough data.
        */
        public $data;
        /**
        * * @var String - Event name triggered in JavaScript when service call successful.
        */
        public $onSuccessEvent;
        /**
        *
        * @var String - Event name triggered in JavaScript when service call fails.
        */
        public $onErrorEvent; } 
?>

En la parte superior de la clase, el listado define:

  • La propiedad success , que indica si el proceso que se invoca tiene éxito o fracasa. Esto no es una indicación de si la llamada de Ajax en sí tiene éxito. Si la llamada de Ajax falla, no obtendrá el objeto JSON por lo que aún necesita manejar las fallas de Ajax con un manejador de fallas de algún tipo. En el ejemplo, éxito indica si el usuario es encontrado durante el proceso de inicio de sesión, o si el perfil del usuario se actualiza correctamente.
  • El array errors , que contiene toda la información acerca de por qué la llamada no tiene éxito. Por ejemplo, si un proceso de inicio de sesión falla, puede contener un mensaje como "No user with that user name and password failed." Al permitir que el objeto ServiceResult pase de nuevo mensajes de error, usted no tiene que modificar el código de los mensajes en su código JavaScript. El código JavaScript simplemente necesita saber que los mensajes de error destinados al usuario están contenidos en este array.
  • La propiedad messages , que también es un array, vuelve a pasar mensajes sin error relacionados al manejador de resultados. Los mensajes pueden ser lo que usted quiera que el usuario sepa relacionado a la llamada de servicio. Usted puede incluir mensajes de éxito, como "You have been logged in." Al mantener separados errores y mensajes genéricos, usted no tiene que determinar si un mensaje se relaciona con un error o si es meramente informativo cuando el manejador de resultados se lo muestra al usuario (así se simplifica la lógica en el lado del cliente).
  • La propiedad data que proporciona un método de pasar datos arbitrarios de vuelta al manejador de resultados. Por ejemplo, si está llamando a un servicio de inicio de sesión, data puede contener datos de perfil de usuario. Mientras la propiedad data en sí es genérica, los manejadores de resultados para la solicitud específica deben hacer suposiciones acerca de los datos contenidos en esta propiedad basándose en la llamada de servicio realizada. En cualquier caso, la propiedad data permite a todos los manejadores de resultados saber dónde buscar esos datos.
  • Las propiedades onSuccessEvent y onErrorEvent son cadenas de caracteres para definir el evento a ser transmitido para una llamada de servicio exitosa o fallida. Otra vez, esto se refiere al proceso y no a la llamada de Ajax en sí. Ejemplos de dichos eventos son userLoggedIn para éxito o loginFailed para falla.

Proporcionar respuestas basadas en eventos a las llamadas de Ajax

El segundo objetivo del sistema es dar respuestas a las llamadas de Ajax basadas en eventos utilizando el modelo de eventos DOM Level 2. Para lograr esto, el ejemplo utiliza uno de los diseños orientados a objetos en JavaScript. Para que el sistema funcione, los objetos individuales deben ser capaces de suscribirse a los eventos DOM Level 2 que son retornados por la llamada de servicio (eventosonSuccessEvent y onErrorEvent pasados de nuevo por el objeto PHP ServiceResult ). Todos los objetos deben suscribirse al mismo objeto que distribuye el evento.

Crear una cadena básica de herencia de objetos para su objeto JavaScript. Crear la cadena de modo que si alguna vez necesita cambiar la biblioteca de JavaScript subyacente, puede hacerlo sin perturbar la interfaz para interactuar con el modelo de eventos DOM Level 2 en sus objetos JavaScript.

El objeto EventDispatcher está en el nivel superior de la cadena de herencia, como se muestra en el Listado 2, y es responsable por la definición de la interfaz JavaScript para interactuar con el modelo de eventos DOM Level 2. También es responsable de asegurar que todos los objetos JavaScript se suscriben al mismo distribuidor.

Listado 2. Objeto EventDispatcher
function EventDispatcher() {

}

EventDispatcher.prototype.addListener = function (eventName, listener) { 
    this.dispatcher.bind(eventName, listener)
}
EventDispatcher.prototype.removeListener = function (eventName, listener) {
    this.dispatcher.unbind(eventName, listener)
} 
EventDispatcher.prototype.dispatch = function (eventName, data) { 
    this.dispatcher.trigger(eventName, data); 
}

EventDispatcher.prototype.dispatcher = jQuery(document);

En el Listado 2:

  • Las funciones addListener y removeListener permiten que usted añada y elimine oyentes desde el objeto de distribución.
  • El parámetro eventName es el evento al que se está suscrito.
  • El parámetro listener es la función a ser registrada.
  • La función dispatch transmite el evento especificado en el parámetro eventName .
  • El parámetro data proporciona un método de hacer pasar datos para cualquier función de oyente suscrita.
  • La propiedad dispatcher se configura para un objeto jQuery que hace referencia al objeto DOM de documento.

Al configurar la propiedad dispatcher para un objeto jQuery haciendo referencia al objeto DOM de documento, todos los eventos de objeto son registrados y transmitidos por el objeto DOM de documento que existe en una página dada. Esto es simplemente una propiedad del objeto EventDispatcher , y todos los métodos hacen referencia a esa propiedad de distribución. Usted puede cambiar la distribución subyacente al cambiar esa propiedad. Usted puede utilizar cualquier objeto que proporcione acceso al modelo de eventos DOM Level 2 por el simple cambio de los métodos de distribución y actualización para corresponder a esa interfaz de distribución (sin interrumpir cualquiera de los objetos que se heredan del objeto EventDispatcher ). Es clave que todos los objetos se suscriban al mismo distribuidor.

El paso siguiente es permitir scripts individuales para heredar las funciones definidas por el objeto EventDispatcher . La empresa en este escenario tiene una convención para que cada página en el sitio tenga un único objeto controlador responsable por la función de esa página. Para cerrar la brecha entre estos controladores individuales de página y el objeto EventDispatcher , cree una clase BaseController que herede desde el objeto EventDispatcher .

Por brevedad, las funciones no específicamente relacionadas a la distribución de eventos se omiten de la muestra de código. Existen otras cosas que los controladores heredan de esta clase.

El Listado 3 muestra la clase BaseController .

Listado 3. Clase BaseController
function BaseController() {

} BaseController.prototype = new EventDispatcher();

Debido a que la propiedad de prototipo BaseController se configura para un objeto EventDispatcher , cualquier controlador específico de página que se hereda de BaseController incluye funciones de distribuidor de eventos.


Centralizar el manejo de resultados de Ajax

La meta final del sistema es centralizar el procesamiento de los resultados de las llamadas de servicio Ajax. Usted puede hacerlo utilizando la función ajaxComplete de jQuery, que se llama automáticamente cada vez que una llamada de Ajax se completa utilizando jQuery. Para garantizar que esto ocurre, coloque el código del listado 4 en un archivo script que usted incluye en cualquier página que haga llamadas de servicio Ajax.

Listado 4. Función ajaxComplete de jQuery
jQuery(document).ajaxComplete(function(event, request, settings) {
    if (settings.dataType == 'json') {
        var dispatcher = new EventDispatcher();
            var result = jQuery.parseJSON(request.responseText);
                if (result) {
                    if (result.success == true && result.onSuccessEvent) {
                        dispatcher.dispatch(result.onSuccessEvent, result)
                    } else if (result.onErrorEvent) {
                        dispatcher.dispatch(result.onErrorEvent, result)
                    }
                }
     }
})

El manejador se registra contra el objeto del documento porque usted sabe que todas las páginas tienen este objeto. Cada vez que se invoca la función, se pasa:

  • Un objeto event de jQuery Ajax (el parámetro evento).
  • El XMLHttpRequest (el parámetro request ).
  • Un objeto representando las configuraciones utilizadas para hacer la llamada de Ajax original (el parámetro settings ).

Para el sistema de ejemplo, usted solo está interesado en el objeto XMLHttpRequest y el objeto settings .

Observe más de cerca el código dentro del manejador en el Listado 4. El manejador verifica primero el objeto settings para ver si el formato del resultado está configurado para JSON. (Recuerde que el propósito de este sistema no es responder a solicitudes simples de carga de contenidos, sino que responder a llamadas de Ajax basadas en servicio, que retorna, codificado en JSON, un ServiceResult ). Si el tipo de datos retornados por la solicitud de Ajax no es JSON, usted no está interesado en responderla y el manejador omite todo el código restante.

Si el tipo de datos es JSON, el código crea una instancia JavaScript de EventDispatcher. Debido a que todas las instancias de la clase EventDispatcher hacen referencia a la misma instancia de distribuidor, los eventos transmitidos por ella desencadenan todos los oyentes registrados en ella por cualquier objeto JavaScript que extiende la clase EventDispatcher .

El código entonces toma una referencia codificada en JSON de la propiedad responseText del objeto de la solicitud (usando la función de jQuery parseJSON ), y la almacena en la variable result .

Suponiendo que el análisis de responseText se realiza correctamente, el manejador comprueba si el objeto result indica que el procedimiento es exitoso comprobando la propiedad success . Si se realiza correctamente, el manejador comprueba si existe definido un onSuccessEvent . Si existe, la instancia de distribuidor event retransmite ese evento junto con el objeto result .

Si el objeto de resultado no indica que el proceso es exitoso, el código comprueba si existe un onErrorEvent. Si está definido, la instancia dispatcher retransmite el evento, pasando de nuevo por el objeto result .

Mediante las funciones ajaxComplete de jQuery, usted ahora puede notificar a su código del lado del cliente para reaccionar basado en eventos. Los objetos del lado del cliente no tienen que registrarse como oyentes en la llamada de Ajax. El código del lado del cliente no tiene siquiera que saber que se está realizando una llamada de servicio. Simplemente tiene que saber qué eventos escuchar y cómo responder a los datos que pasan de nuevo por él.


Ejemplo de llamada Ajax

En esta sección, una simple llamada de servicio de inicio de sesión de Ajax muestra cómo las piezas del sistema interactúan. Por simplicidad, se supone que usted ha incluido correctamente todos los archivos necesarios de PHP y JavaScript.

Cree un archivo PHP que represente el servicio invocado y retorne, codificado en JSON, un objeto ServiceResult . El Listado 5 muestra un ejemplo.

Listado 5. Archivo PHP
<?php
    $ctrl = new LoginController();
    $result = new ServiceResult();
    $result->success = $ctrl->login($_POST['username'], $_POST['password']);
    $result->errors = $ctrl->errors;
    $result->onSuccessEvent = 'refreshPage';
    $result->onErrorEvent = 'userLoginFailed';
    echo json_encode($result); 
?>

Es necesario un objeto JavaScript que invoque el servicio. Para este ejemplo, se supone que usted ya creó un formulario HTML de inicio de sesión. Llame el objeto responsable por hacer la llamada de servicio LoginController, como en el Listado 6. Llamada de servicio

Listado 6. LoginController
LoginController.prototype.login = function() {
    var self = this;
    var username = jQuery('#loginForm .email').val()
    var password = jQuery('#loginForm .password').val()
    var data = { 
        username:username,
        password:password,
    };
    jQuery.ajax({
        url:'login.php',
        async:true,
        service:'login',
        type:'post',
        dataType:'json',
        data:data
    });
function LoginController() {
    var self = this;
    jQuery(document).ready(function() {
        self.onLoad();
    })
}

LoginController.prototype = new BaseController();

LoginController.prototype.onLoad = function() {
    var self = this;
    this.addListener('userLoginFailed', function(event, result) {
        self.onLoginFailed(result);
    })
}

LoginController.prototype.onLoginFailed = function(result) {
    jQuery('#errorDisplay').html(result.errors[0]);
}
            }

Debido a que este sistema responde a las llamadas de servicio Ajax mediante eventos, usted no pasa manejadores de resultados y fallas a la llamada jQuery.ajax .

Ahora que ha invocado el servicio, usted desea permitir a los objetos responder a onSucessEvents y onErrorEvents definidos el el objeto ServiceResult que es retornado por el servicio de inicio de sesión. Es necesario que los objetos que responden hereden desde EventDispatcher, que es heredable extendiendo BaseController. En este caso, el objeto LoginController muestra al usuario un mensaje explicando que el intento de inicio de sesión ha fallado. Si el inicio de sesión tiene éxito, el objeto AppController responde actualizando la página. Empiece con el LoginController, como en el Listado 7. Añadiendo oyentes de

Listado 7. LoginController
function LoginController() { 
    var self = this;
    jQuery(document).ready(function() {
        self.onLoad();
    }) 
}

LoginController.prototype = new BaseController();

LoginController.prototype.onLoad = function() {
    var self = this;
    this.addListener('userLoginFailed', function(event, result) {
        self.onLoginFailed(result);
    })
}

LoginController.prototype.onLoginFailed = function(result) {
    jQuery('#errorDisplay').html(result.errors[0]);
}

El constructor invoca una función onLoad . Esto no es clave para el ejemplo, pero se incluye porque puede ser necesario que este controlador haga referencia a objetos DOM no disponibles hasta que se cargue la página.

Dos elementos claves a notar son:

  • El objeto prototipo del LoginController es un BaseController. Por lo tanto, también hereda los métodos de EventDispatcher .
  • Utilice el método EventDispatcher addListener en la función onLoad . El evento que se pasa coincide con el onSuccessEvent retornado por el objeto ServiceResult que es retornado por el servicio de inicio de sesión. La función de manejador registrada se pasa junto al objeto result , que es el objeto ServiceResult codificado en JSON, retornado por el servicio de inicio de sesión.

El método onLoginFailed utiliza el array de error del objeto result para mostrar un mensaje al usuario. El manejo de un inicio de sesión fallido es atendido.

Cuando el inicio de sesión del usuario es exitosa, usted desea que la página se actualice para que los datos de inicio de sesión del usuario sean actualizados. Sin embargo, hay otras ocasiones en que puede querer responder a llamadas de servicio al actualizar la página, por ejemplo, cuando los usuarios actualicen sus perfiles. En vez de tener varios controladores con manejadores de resultados que actualizan la página, mantenga esta función en un controlador de nivel superior. Entonces, las llamadas de servicio que quieran actualizar la página para actualizar los datos de la sesión pueden hacerlo simplemente distribuyendo el tipo de evento refreshPage como el onSucessEvent. Para hacerlo, repita el proceso que utilizó para el LoginController en el AppController, como en el Listado 8. Objeto

Listado 8. AppController
function AppController() {
    var self = this;
    jQuery(document).ready(function() {
        self.onLoad();
    })
}

AppController.prototype = new BaseController();

AppController.prototype.onLoad = function () {
    this.addListener('refreshPage', function(event, user) {
    window.location.reload()
    })
}

Otra vez, el AppController simplemente configura su prototipo a una instancia de BaseController y registra una función de oyente utilizando addListener para el evento refreshPage .

Ahora que ha creado los objetos que responden y registrado los oyentes de eventos, incluya el script con la función onAjaxComplete de manejador de eventos. Al completarse la llamada de servicio, el manejador onAjaxComplete distribuye el evento apropiado que notifica a la función de manejador registrada.

No es necesario que el LoginController y el AppController tengan conocimiento uno del otro. Incluso no saben que están respondiendo a una llamada de servicio --y mucho menos a la misma llamada de servicio.


Resumen

En este artículo se examinó un sistema para hacer llamadas de servicio Ajax que:

  • Estandariza el formato del resultado de los objetos retornados por las llamadas de servicio Ajax utilizando un objeto ServiceResult PHP que es retornado en un formato codificado en JSON.
  • Proporciona respuestas basadas en eventos a las llamadas de Ajax diseñando un objeto EventDispatcher de JavaScript que permite a todas las subclases registrar y responder a eventos en un distribuidor común.
  • Centraliza el manejo de los resultados de Ajax mediante un manejador onAjaxComplete de jQuery para actuar como un manejador centralizado que transmite los eventos especificados en el objeto ServiceResult retornado por la llamada de servicio.

Un ejemplo de llamada Ajax mostró cómo usted puede crear dos objetos JavaScript, que no tienen conocimiento uno de otro, para responder a la misma llamada de servicio.

Recursos

Aprender

Obtener los productos y tecnologías

Comentar

Comentarios

developerWorks: Ingrese

Los campos obligatorios están marcados con un asterisco (*).


¿Necesita un IBM ID?
¿Olvidó su IBM ID?


¿Olvidó su Password?
Cambie su Password

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


La primera vez que inicie sesión en developerWorks, se creará un perfil para usted. La información en su propio perfil (nombre, país/región y nombre de la empresa) se muestra al público y acompañará a cualquier contenido que publique, a menos que opte por la opción de ocultar el nombre de su empresa. Puede actualizar su cuenta de IBM en cualquier momento.

Toda la información enviada es segura.

Elija su nombre para mostrar



La primera vez que inicia sesión en developerWorks se crea un perfil para usted, teniendo que elegir un nombre para mostrar en el mismo. Este nombre acompañará el contenido que usted publique en developerWorks.

Por favor elija un nombre de 3 - 31 caracteres. Su nombre de usuario debe ser único en la comunidad developerWorks y debe ser distinto a su dirección de email por motivos de privacidad.

Los campos obligatorios están marcados con un asterisco (*).

(Por favor elija un nombre de 3 - 31 caracteres.)

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


Toda la información enviada es segura.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=90
Zone=
ArticleID=837106
ArticleTitle=Realiza llamadas de servicio Ajax con PHP, jQuery y JSON
publish-date=09252012