martes, 15 de abril de 2008

Tutorial de Flex 2 con WebOrb PHP y acceso a base de datos

En este tutorial aprenderemos desde cero a obtener datos desde una base de datos de MySQL y a representarlos en un DataGrid de Flex, donde se puedan modificar, eliminar e insertar nuevos registros, mediante una clase de PHP. Este tutorial es una adaptación del tutorial de bases de datos con flash y amfphp de Maikel, por ser extremadamente completo y además es de donde yo aprendí.

Primero instalaremos WebOrb, un sistema parecido a AMFPHP, que funciona con Flex, y nos permite acceder, de manera limitada, a los Flex Data Services, en concreto a los remoteObjects, instancias de una clase de un lenguaje de servidor cuyas propiedades y métodos se pueden llamar desde Flex. WebOrb tiene algunos bugs, pero resulta muy rápido trabajar con él (por ejemplo no hacen falta los methodTable de AMFPHP).

Después crearemos una base de datos desde phpmyadmin, agregaremos a WebOrb la clase misquerys de Maikel, y por último haremos una aplicación parecida a la suya en Flex.

Para poder realizar el tutorial es imprescindible conocer, al menos en parte, el funcionamiento de flex, php y las bases de datos. Es recomendable haber hecho algún tuto similar para flash, como el de Maikel, que mencioné arriba.

Para empezar necesitas un servidor que admita PHP 5 (no, con PHP4 no vale) y MySQL. Un programa para crear un servidor local que cumple estas características es wamp (pero no EasyPHP u otros) .

Además usaremos FlexBuilder2 para configurar el proyecto y obtener el swf final. Configurar el proyecto con algún otro framework es bastante complicado, pero siempre pueden probar FlashDevelop.
Instalación de WebOrb

Instalar WebOrb es muy sencillo. Además tiene la ventaja de que podemos tratar en ActionScript instancias de una clase de php con toda naturalidad, y sin tanto código como hace falta para AMFPHP.

Simplemente tenemos que descargar la última versión para php desde aquí: http://www.themidnightcoders.com/weborb/php/weborb-php-latest.zip

Ahora tenemos que descomprimir el archivo .zip que hemos conseguido en la raiz de nuestro sitio (la carpeta www donde tenemos nuestro index, vamos).

Ahora, para comprobar que todo funciona abrimos el archivo http://tusitio/Examples/FlexRemoting/main.html, que si trabajamos en local será http://localhost/Examples/FlexRemoting/main.html. Los 5 primeros test deberían funcionar sin problemas y el sexto una vez hayamos ejecutado el desde phpmyadmin (u otra cosa parecida) el archivo siguiente archivo: DIRECTORIOWEB\Services\weborb\tests\northwind.sql.

Aquí está el tutorial en inglés de la web oficial.
Preparación de la Base de Datos:

Crea una base de datos para el ejemplo (o usa la que tengas si es de alojamiento gratuito), en mi caso llamaré a la base de datos flexphp.

Crea una tabla con 3 campos, en mi caso le di el nombre de prueba, los campos deberán tener esta descripción:
Nombre

Tipo de Dato

Características adicionales
id

bigint

not null, auto_increment, clave primaria
nombre

text

null
comentarios

text

null

Nota: Pueden crear la base de datos ejecutando el archivo flexphp_bd/flexphp.sql de los archivos det tutorial.
Crear y agregar una clase de php a WebOrb

Bien, el primer paso es crear la clase de php. En la carpeta Services de nuestro directorio principal creamos un archivo php llamado "misquerys.php":


class misquerys{


function insertar($nombre,$comentarios){
$nombre = utf8_encode($nombre);
$comentarios = utf8_encode($comentarios);
$sql= "INSERT INTO prueba (id,nombre,comentarios) VALUES ('','".$nombre."','".$comentarios."')";
return $this->query($sql,1); //retornamos el id de nuestro registro insertado

}

function eliminar ($id){
$sql= "DELETE FROM prueba WHERE id='".$id."' LIMIT 1";
return $this->query($sql,0);
}

function modificar ($id,$nombre,$comentarios){
$nombre = utf8_encode($nombre);
$comentarios = utf8_encode($comentarios);
//creamos la cadena de nuestra sentencia(query) SQL

$sql = "UPDATE `prueba` SET `nombre` ='".$nombre."', `comentarios` ='".$comentarios."' WHERE `id` =".$id." LIMIT 1;";
return $this->query($sql,0);
}

function consulta (){
$sql = "SELECT * FROM prueba ORDER BY id ";
return $this->query($sql,0);
}

function query ($sql, $accion){
$conex= mysql_connect("tuhost","user","password") or die("no se puede conectar porque ".mysql_error());
mysql_select_db("flexphp");
$result= mysql_query($sql,$conex);
if ($accion && result){
$result= mysql_insert_id();
}
mysql_close($conex);
return $result;
}

}

?>

Explicación:

Una descripción detallada de cada método:
Nombre

Parámetros

Explicación

Valor de Retorno
insertar
$nombre, $comentarios Inserta los parámetros a los respectivos campos de la tabla, codificando con utf8_encode los parámetros para entender la codificación que hace flash con el escape para mandarle los parámetros y crea la respectiva sentencia SQL. Retorna el id del registro insertado en tal caso de que se haya hecho exitosamente.
eliminar
$id Crea la sentencia SQL DELETE con la condición de que id sea el que recibio. Retorna 1 si fue exitosa la operación o 0 en caso contrario.
modificar
$id, $nombre, $comentarios Crea la sentencia SQL UPDATE, buscando el registro cuyo campo id corresponda con $id y actualiza los campos nombre y comentarios con los respectivos parámetros. Retorna 1 si fue exitosa la operación o 0 en caso contrario.
consulta
ninguno Crea la sentencia SQL SELECT para seleccionar todos los registros de la tabla. Retorna un RecordSet con los registros.
query
$sql, $accion $sql será la sentencia SQL que ejecutará, $accion es para hacer una excepción de que si la sentencia es un INSERT.

* Para consulta: retorna un recordSet.
* Para eliminar y modificar: 1 o 0 en caso de que haya sido exitosa la operación.
* Para insertar: hay una excepción donde el parámetro $accion valdrá 1 al invocar la función query y es para indicar que retornará el id del último registro insertado.

Noten que en el código, en la función query, donde pone "tuhost, "user" y "password" tendrán que sustituirlos por sus datos.

Ahora tenemos que decirle a WebOrb que este servicio existe:

Vamos a la carpeta principal>Weborb>WEB-INF>flex y abrimos el archivo remoting-config.xml. En él, al final del código, antes del último añadimos lo siguiente



misquerys




Configuración del proyecto de Flex

Vamos con Flex Builder:

File> New > Flex Project.

En el primer cuadro de diálogo How will your Flex application access data? seleccionamos Flex Data Services, y después Compile application locally in Flex Builder. Hacemos click en Next.

Después en el siguiente diálogo, en el cuadro "Root folder" tendremos que decir cuál es la ruta de la carpeta weborb, en la que está el servidor. La mía es: C:\wamp\www\Weborb . Y en el cuadro "Root URL" cuál será la URL del servidor, en este caso, http://localhost/Weborb . De nuevo, hacemos click en Next,

Ahora nos pide que demos un nombre al proyecto y la carpeta en la que se guardará. Cuando lo hayamos hecho, volvemos a pinchar en Next.

Allí, dejamos vacío el campo "Main source folder" y donde pone Output folder, ponemos la carpeta en la que deseamos que esté el archivo generado (tendrá que ser dentro del directorio web). Por ejemplo: C:\wamp\www\Examples. También tendremos que darle la ruta que tendrá cuando aparezca en el navegador al darle al botón de compilar, en el caso anterior sería esta: http://localhost/Examples.

De nuevo, esto pueden verlo en inglés en la ayuda de WebOrb.

Y por fin podemos empezar a programar mxml.
Creación de la aplicación de Flex.

Como es un poco difícil de separar por partes, pondré todo el código comentado y luego una explicación más general (y creo que es más rápido que andar explicando con el modo diseñador de Flex). Así que lean atentamente los comentarios (Texto en verde) del siguiente código:



creationComplete="remoteObject.consulta()">






















































































//Importamos la clase ArrayCollection, que usaremos en el DataProvider.
import mx.collections.ArrayCollection;
//Importamos la clase para trabajar con las comlumnas del dataGrid, que será necesaria para definir
//función con la que rellenaremos el dataGrid
import mx.controls.dataGridClasses.DataGridColumn;
//Importamos las clases para trabajar con los remoteObjects de WebOrb
import mx.rpc.remoting.*;
import mx.controls.*;
import mx.rpc.events.*
//Importamos los detectores de eventos
import flash.events.Event;
//Una variable que necesitaremos para saber qué operación realizar
private var action:Number;
//
//
//
//Esta variable servirá para definir el índuce en el que tendrá que estar el datagrid
//alrealizar una consulta
private var dgIndex:int=0;
//
//
//Esta variable contiene el resultado de la petición. Su tipo de datos será
// ArrayCollection, el recomendado para dataProviders.
//El metatag Bindable hará que la variable se pase por referencia:
//si ella cambia, las demás cambian.
[Bindable]
private var resultado:ArrayCollection
//
//
//
//Al ejecutarse una consulta...
private function consulta_Result(event:ResultEvent):void{
//rellenamos la variable resultado con los datos obtenidos en la consulta,
//tratando de convertirlos en un Array usando el operador "as".
resultado=new ArrayCollection(event.result as Array);
//Rellenamos el dataGrid con los datos que hemos recibido
datagrid.dataProvider=resultado;
//Si hay al menos un registro...
if (resultado[0]){
estadotxt.text="Consulta completada";
//Seleccionamos el primer elemento del datagrid.
datagrid.selectedIndex=dgIndex;
//Mostramos los botones de Eliminar y Modificar por si estaban deactivados
btnModificar.enabled=btnEliminar.enabled=true;
//Si no...
}else{
//Desactivamos los botones de Eliminar y Modificar
btnModificar.enabled=btnEliminar.enabled=false;
estadotxt.text="No hay registro que mostrar";
}
}
//
//
//
//El resultado de insertar un registro:
private function insertar_Result(event:ResultEvent):void{
//Si todo salió bien...
if (event.result){
estadotxt.text = "La inserción se realizó exitosamente";
dgIndex=datagrid.dataProvider.length;
//Volvemos a cargar los datos actulizados
remoteObject.consulta();
} else {
estadotxt.text = "La inserción no fue completada, intente de nuevo";
}
}
//Lo mismo para las funciones modificar y eliminar
private function modificar_Result(event:ResultEvent):void{
if (event.result){
estadotxt.text = "La modificación se realizó exitosamente";
remoteObject.consulta();
} else {
estadotxt.text = "La modificación no fue completada, intente de nuevo";
}
}
private function eliminar_Result(event:ResultEvent):void{
if (event.result){
estadotxt.text = "El registro se eliminó correctamente";
remoteObject.consulta();
} else {
estadotxt.text = "El registro no se eliminó, la operación falló";
}
}
//
//
//
//Muestra el error al no poder ejecutarse alguna función
public static function onFault(event:FaultEvent):void
{
Alert.show(event.fault.faultString, 'Error');
}
//
//
//
//Esto pasará cada vez que se pusle un botón:
private function onButtonPress(event:Event):void{
//Según el botoón que hayamos pulsado...
switch (event.target){
case btnConsultar:
remoteObject.consulta();
estadotxt.text="Consultando..."
break;
case btnInsertar:
//Usamos la variable action para saber a cuál de las acciones posibles nos referimos,
//para evaluarlas después, al presionar el botón Si
action = 1;
//Nos ponemos en el estado donde están todos los menús
currentState="commands";
//No necesitamos que se pueda insertar el id
idtxt.text="##";
//Borramos los otros campos de texto
nombretxt.text= "";
comentariostxt.text= "";
preguntatxt.text="¿Desea insertar este registro?";
nombretxt.editable=true;
comentariostxt.editable=true;
break;
case btnModificar:
//Nos ponemos en el estado donde están todos los menús
currentState="commands";
//Mostramos el elemento seleccionado en el DataGrid, con los carácteres codificados
//correctamente
idtxt.text= datagrid.selectedItem.id.toString();
nombretxt.text= unescape(datagrid.selectedItem.nombre);
comentariostxt.text= unescape(datagrid.selectedItem.comentarios);
action = 2;
preguntatxt.text="¿Desea modificar este registro?";
nombretxt.editable=true;
comentariostxt.editable=true;
break;
case btnEliminar:
//Nos ponemos en el estado donde están todos los menús
currentState="commands";
idtxt.text= datagrid.selectedItem.id.toString();
nombretxt.text= unescape(datagrid.selectedItem.nombre);
comentariostxt.text= unescape(datagrid.selectedItem.comentarios);
action = 3;
preguntatxt.text="¿Desea eliminar este registro?";
nombretxt.editable=false;
comentariostxt.editable=false;
break;
case btnSi:
switch (action){
//Si la acción es...
case 1:
//Realizamos la función php que hemos pedido.
remoteObject.insertar(escape(nombretxt.text), escape(comentariostxt.text));
break;
case 2:
remoteObject.modificar(idtxt.text,escape(nombretxt.text), escape(comentariostxt.text));
break;
case 3:
remoteObject.eliminar(idtxt.text);
break;
}
//Volvemos al estado principal
currentState="";
break;
case btnNo:
currentState="";
break;

}
}
//Función para mostrar correctamente el DataGrid
public function dataGridLabelFunction(item:Object, column:DataGridColumn):String{
//Devuelve lo mismo, pero aplicando un unescape
return unescape(item[column.dataField].toString());
}
//Función para ordenar los datos de la columna id de manera numérica, no alfabética:
public function numericSort(a:*,b:*):int{
var nA:Number=Number(a.id);
var nB:Number=Number(b.id);
if (nA return -1;
}else if (nA>nB){
return 1;
}else {
return 0;
}
}
]]>



En el primer estado coloco un Panel, que ocupará todo el espacio disponible del swf (hago esto con el width=heigth=100%) y dentro un DataGrid que también intentará ocupar todo el espacio.

El DataGrid (datagrid) será quien muestre la información. Sus tres columnas, que muestran cada una la información del objeto del dataProvider que les indica el parámetro dataField, aunque aquí sirve más bien de indicador ya que las columnas tienen una labelFunction para mostrar la información decodificada (ya que si no hay problemas con los carácteres raros y es la solución mas fácil). La columna que muestra la propiedad id tiene una sortCompareFunction, para que al presionar sobre ella los datos se ordenen de forma numérica, no alfabética.

Debajo del DataGrid coloco un Grid de una fila, que permitirá mantener los botones que lleva dentro distribuidos según el espacio disponible.

Debajo hay una caja de texto que nos irá informando sobre el transcurso de las operaciones.

Los tres últimos paneles llevan al segundo estado, que añade tres cajas de texto y los botones para confirmar o cancelar la operación, elimina los botones principales y deshabilita el datagrid.

Según el botón que hayamos pulsado, las cajas de texto serán o no editables y mostrarán unos datos u otros. Al insertar un registro, no podremos usar el campo id, ni modificarlo al pulsar el botón modificar. Con el botón eliminar se verán los tres campos pero ninguno será editable.

Al pulsar el botón No simplemente volveremos al estado principal y al presionar el btnSi además realizaremos la operación solicitada con el Remote Object de php.
Conclusiones

Flex crea un nuevo mundo de posiblidades en las aplicaciones de Internet, permitiendo un manejo de datos potente y fácil y una rapidísima forma de montar proyectos que en Flash costarían mucho más tiempo (eso sí, una vez que te acostumbras a programar con él ;) ).
Información adicional

Archivos del tutorial

Fte: http://www.cristalab.com/tutoriales/204/tutorial-de-flex-2-con-weborb-php-y-acceso-a-base-de-datos

No hay comentarios:

Publicar un comentario