En este tutorial veremos cómo acceder a los registros de una base de datos MySQL desde Flex utilizando AMFPHP. Realizaremos un pequeño Sistema de Gastos en el cual haremos inserciones a la base de datos, consultas, modificaciones y eliminación de registros. Si no sabes qué es y para qué sirve el AMFPHP, visita nuestro tutorial de Introducción a AMFPHP.
En esta ocasión no veremos a detalle todo el código que involucra el sistema; sin embargo, puedes encontrar al final de esta entrada los archivos fuentes.
El sistema funciona de la siguiente manera:
- El usuario primero da de alta tipo de monedas en la Sección de Divisas (Su nombre y símbolo).
- Posteriormente el usuario puede dar de alta servicios en la Sección de Servicios (valga la redundancia). Estos servicios son opcionales y pueden seleccionarse cuando un usuario introduce un gasto en el sistema. Ejemplo de servicios: teléfono, gas, luz, educación, restaurantes, entretenimiento, etc.
- Dentro del sistema, el usuario puede dar de alta gastos e ingresos. Estos gastos o ingresos requieren de la fecha, cantidad y divisa con la cual se realizó la operación. De manera opcional se puede introducir un concepto (descripción) y se puede seleccionar un servicio (si se trata de un gasto).
Comenzaremos con el script de nuestra base de datos:
# Host: localhost Database: codmetr_gastos # ------------------------------------------------------ # Server version 4.1.22-community-nt # # Table structure for table tbldivisa # CREATE TABLE `tbldivisa` ( `INTNUMDIVISA` int(11) NOT NULL auto_increment, `STRNOMBRE` varchar(30) NOT NULL default '', `STRSIMBOLO` varchar(5) character set utf8 default NULL, PRIMARY KEY (`INTNUMDIVISA`) ) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=latin1; # # Table structure for table tbloperacion # CREATE TABLE `tbloperacion` ( `INTNUMOPERACION` int(11) NOT NULL auto_increment, `DTMFECHA` date NOT NULL default '0000-00-00', `STRCONCEPTO` varchar(100) default NULL, `NUMCANTIDAD` decimal(10,2) default NULL, `INTNUMDIVISA` int(11) NOT NULL default '0', `INTNUMSERVICIO` int(11) default NULL, `INTOPERACION` int(11) NOT NULL default '0', PRIMARY KEY (`INTNUMOPERACION`) ) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=latin1; # # Table structure for table tblservicio # CREATE TABLE `tblservicio` ( `INTNUMSERVICIO` int(11) NOT NULL auto_increment, `STRNOMBRE` varchar(50) default NULL, PRIMARY KEY (`INTNUMSERVICIO`) ) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
Del código anterior podemos observar que tenemos una tabla tbldivisa, en la cual almacenamos los tipos de moneda que utilizamos. Una tabla tblservicio donde guardamos los servicios ingresados; y una tabla tbloperacion en donde almacenamos las operaciones (gastos e ingresos). Las relaciones entre divisas y servicios con respecto a las operaciones están dadas por el ID de estas tablas (INTNUMDIVISA e INTNUMSERVICIO).
Ahora veremos el código PHP de nuestra que funcionará como servicio:
<?php
class SistemaGastos
{
var $sqlstring = "";
var $server = "localhost";
var $user = "usuario_database";
var $pass = "contrasena_database";
var $database = "codigometr_gastos";
var $db = 0;
var $rs = 0;
var $row = 0;
var $recordcount = 0;
var $EOF = true;
function _conectarBaseDatos()
{
$this->db = mysql_connect($this->server,$this->user,$this->pass);
mysql_select_db($this->database,$this->db) or die(mysql_error());
}
function _ejecutarQuery($strSql)
{
$this->sqlstring = $strSql;
$this->_exec_command();
return $this->lastid;
}
function _exec_command()
{
if ($this->db && $this->sqlstring!="")
{
$this->rs = mysql_query($this->sqlstring,$this->db);
if ($this->rs)
{
$this->EOF = true;
$this->recordcount = mysql_affected_rows();
$this->lastid = mysql_insert_id();
}
else
{
$this->recordcount = 0;
$this->EOF = true;
}
}
else
{
$this->recordcount = 0;
$this->EOF = true;
}
}
function _destruir()
{
if($this->db)
mysql_close($this->db);
}
/* Métodos para la tabla tbldivisa */
function insertarDivisa($nombre, $simbolo)
{
$this->_conectarBaseDatos();
$sql = "Insert into `tbldivisa` (`STRNOMBRE`, `STRSIMBOLO`) VALUES ('" . $nombre . "', '" . $simbolo . "')";
$this->_ejecutarQuery($sql);
$this->_destruir();
}
function modificarDivisa($idDivisa, $nombre, $simbolo)
{
$this->_conectarBaseDatos();
$sql = "Update `tbldivisa` set `STRNOMBRE` = '" . $nombre . "', `STRSIMBOLO` = '" . $simbolo . "' where `INTNUMDIVISA` = " . $idDivisa;
$this->_ejecutarQuery($sql);
$this->_destruir();
}
function eliminarDivisa($idDivisa)
{
$this->_conectarBaseDatos();
$sql = "Delete from `tbldivisa` where `INTNUMDIVISA`= " . $idDivisa;
$result = mysql_query($sql);
$sql = "Delete from `tbloperacion` where `INTNUMDIVISA`= " . $idDivisa;
mysql_query($sql);
$this->_destruir();
return $result;
}
function obtenerDivisas()
{
$this->_conectarBaseDatos();
$sql = "Select INTNUMDIVISA, STRNOMBRE, STRSIMBOLO from tbldivisa ORDER BY STRNOMBRE asc";
$result = mysql_query($sql);
while ($row = mysql_fetch_object($result))
{
$divisas[] = $row;
}
$this->_destruir();
return($divisas);
}
/* Métodos para la tabla tblservicio */
function insertarServicio($nombre)
{
$this->_conectarBaseDatos();
$sql = "Insert into `tblservicio` (`STRNOMBRE`) VALUES ('" . $nombre . "')";
$this->_ejecutarQuery($sql);
$this->_destruir();
}
function modificarServicio($idServicio, $nombre)
{
$this->_conectarBaseDatos();
$sql = "Update `tblservicio` set `STRNOMBRE` = '" . $nombre . "' where `INTNUMSERVICIO` = " . $idServicio;
$this->_ejecutarQuery($sql);
$this->_destruir();
}
function eliminarServicio($idServicio)
{
$this->_conectarBaseDatos();
$sql = "Delete from `tblservicio` where `INTNUMSERVICIO`= " . $idServicio;
$result = mysql_query($sql);
$sql = "Delete from `tbloperacion` where `INTNUMSERVICIO`= " . $idServicio;
mysql_query($sql);
$this->_destruir();
return $result;
}
function obtenerServicios()
{
$this->_conectarBaseDatos();
$sql = "Select INTNUMSERVICIO, STRNOMBRE from tblservicio ORDER BY STRNOMBRE asc";
$result = mysql_query($sql);
while ($row = mysql_fetch_object($result))
{
$servicios[] = $row;
}
$this->_destruir();
return($servicios);
}
/* Métodos para la tabla tbloperacion */
function insertarOperacion($fecha, $concepto, $cantidad, $id_divisa, $servicio, $operacion)
{
$this->_conectarBaseDatos();
$sql = "INSERT INTO `tbloperacion` (DTMFECHA, STRCONCEPTO, NUMCANTIDAD, INTNUMDIVISA, INTNUMSERVICIO, INTOPERACION) VALUES ('" . $fecha . "','" . $concepto . "'," . $cantidad . "," . $id_divisa . "," . $servicio . "," . $operacion . ")";
$result = $this->_ejecutarQuery($sql);
$this->_destruir();
return $result;
}
function actualizarOperacion($idOperacion, $fecha, $concepto, $cantidad, $id_divisa, $servicio, $operacion)
{
$this->_conectarBaseDatos();
$sql = "Update `tbloperacion` set `DTMFECHA` = '" . $fecha . "', `STRCONCEPTO` = '" . $concepto . "', `NUMCANTIDAD` = " . $cantidad . ", `INTNUMDIVISA` = " . $id_divisa . ", `INTNUMSERVICIO` = " . $servicio . ", `INTOPERACION` = " . $operacion . " where `INTNUMOPERACION` = " . $idOperacion;
$result = $this->_ejecutarQuery($sql);
$this->_destruir();
return $result;
}
function eliminarOperacion($idOperacion)
{
$this->_conectarBaseDatos();
$sql = "Delete from `tbloperacion` where `INTNUMOPERACION`= " . $idOperacion;
$result = mysql_query($sql);
$this->_destruir();
return $result;
}
function obtenerOperaciones()
{
$this->_conectarBaseDatos();
$sql = "Select * from `tbloperacion` ORDER BY DTMFECHA asc";
$result = mysql_query($sql);
while ($row = mysql_fetch_object($result))
{
$operaciones[] = $row;
}
$this->_destruir();
return($operaciones);
}
function realizarConsulta($servicio, $divisa, $concepto, $fecha1, $fecha2, $cantidad1, $cantidad2, $operacion)
{
$this->_conectarBaseDatos();
$sql = "Select top.DTMFECHA, top.STRCONCEPTO, top.NUMCANTIDAD, top.INTNUMDIVISA, top.INTNUMSERVICIO, top.INTOPERACION, td.STRNOMBRE, td.STRSIMBOLO FROM tbloperacion AS top, tbldivisa as td WHERE td.INTNUMDIVISA = top.INTNUMDIVISA";
/* Selección Concepto */
if($concepto != "")
$sql .= " AND top.STRCONCEPTO LIKE '%" . $concepto . "%'";
/* Selección Divisa */
if($divisa != 0) {
$sql .= " AND top.INTNUMDIVISA = " . $divisa . " AND td.INTNUMDIVISA = " . $divisa;
}
/* Selección Servicio */
if($servicio != 0) {
$sql .= " AND top.INTNUMSERVICIO = " . $servicio;
}
if($cantidad1 != "" && $cantidad2 != "")
{
if($cantidad1 <= $cantidad2)
$sql .= " AND top.NUMCANTIDAD BETWEEN " . $cantidad1 . " AND " . $cantidad2;
else
$sql .= " AND top.NUMCANTIDADBETWEEN " . $cantidad2 . " AND " . $cantidad1;
}
else if($cantidad1 != "")
$sql .= " AND top.NUMCANTIDAD = " . $cantidad1;
else if($cantidad2 != "")
$sql .= " AND top.NUMCANTIDAD = " . $cantidad2;
/* Selección Operación */
if($operacion == 3)
$sql .= " AND top.INTOPERACION >= 1 AND top.INTOPERACION <= 2";
else
$sql .= " AND top.INTOPERACION = " . $operacion;
if($fecha1 != NULL || $fecha2 != NULL)
{
if($fecha1 != "" && $fecha2 == "")
{
$sql .= " AND top.DTMFECHA = '" . $fecha1 . "'";
}
else
{
$sql .= " AND top.DTMFECHA >= '" . $fecha1 . "' AND top.DTMFECHA <= '" . $fecha2 . "'";
}
}
$sql .= " ORDER BY top.DTMFECHA asc, top.NUMCANTIDAD asc";
$result = mysql_query($sql);
while ($row = mysql_fetch_object($result))
{
$registros[] = $row;
}
$this->_destruir();
return($registros);
}
}
?>
Los métodos que inician con guión bajo (_) son métodos privados y por lo tanto no podemos acceder a ellos desde Flex. Los otros métodos insertarDivisa, modificarDivisa, eliminarDivisa, obtenerDivisas, etc. son métodos públicos a los cuales accedemos desde Flex.
Observa cómo en los métodos obtenerXXX almacenamos los registros en un arreglo y lo regresamos a nuestra aplicación. Los queries en realidad son muy sencillos, por lo que no requieren explicación; solamente observa cómo en los métodos eliminarServicio y eliminarDivisa hacemos el borrado de los registros que involucran estos registros en la tabla tbloperacion. Esto es para evitar que haya inconsistencia en la base de datos.
Ahora veremos el código principal de nuestra aplicación en Flex.
Para poder hacer uso del AMFPHP necesitamos de las librerías NetConnection y Responder.
import flash.net.NetConnection; import flash.net.Responder;
Para poder acceder a los servicios en AMFPHP debemos especificar la ruta en la cual se encuentra el archivo gateway.php de AMFPHP:
private var gateway:String = "http://www.codigometropoli.com/wp-content/uploads/2008/10/SistemaGastos/AMFPHP1.9/gateway.php"; private var connection:NetConnection; private var responder:Responder;
Al iniciar la aplicación debemos llamar a la función connect pasándole como parámetro el gateway:
function iniciaAplicacion()
{
connection = new NetConnection;
connection.connect(gateway);
}
Dentro del constructor de la clase Responder debemos especificar las funciones que se ejecutarán en caso de que la llamada haya sido sido exitosa y en caso de que haya existido un error. Para hacer la llamada al servicio utilizamos la función call; a la cual pasamos como parámetro el nombre de la función que queremos llamar y que se encuentra en la clase SistemaGastos.php, nuestro objeto de la clase Responder y los parámetros que debemos pasarle a la función:
responder = new Responder(insertarOperacionResult, errorConsulta);
connection.call("SistemaGastos.insertarOperacion", responder, param1, param2, param3...);
Del código anterior podemos deducir que el nombre de nuestra clase en PHP es SistemaGastos y que tiene un método público llamado insertarOperacion.
En caso de que la llamada haya sido exitosa, se ejecutará el método insertarOperacionResult:
private function insertarOperacionResult(result:Object):void {
if(result.toString() != "0")
{
restablecerForma();
Alert.show("Registro almacenado", "Aviso");
}
else
Alert.show("No se pudo almacenar el registro", "Error");
}
En caso contrario, si existiera algún error en la llamada al servicio, la función errorConsulta se ejecutará:
private function errorConsulta(fault:Object):void {
Alert.show(fault.description, "Error");
CursorManager.removeBusyCursor();
}
Nuestra forma de alta de operación (Gasto o Ingreso) será la siguiente:

De la imagen anterior puedes saber qué tipo de información estamos solicitando, cuál es la obligatoria (Fecha, Cantidad y Divisa), así como la forma en qué estamos mostrando al usuario las divisas y servicios dados de alta (por medio de ComboBox).
Las partes de código mostradas forman parte de la llamada a una función que se encarga de insertar un registro. El código no tendrá variación cuando se trate de modificar un registro; y cuando se trate de eliminar un registro solamente le pasaremos el ID del registro a eliminar. ¿Pero qué pasa cuando se trata de una consulta? ¿Cómo obtenemos los registros registros regresados por la base de datos? ¿Cómo mostramos esos registros?
Si leíste el tutorial sobre Componente DataGrid y las cuatro entregas del tutorial Sistema de Clientes en AIR sabrás que podemos recorrer cada uno de los registros obtenidos de la base de datos y almacenarlos en un arreglo por medio del método push; posteriormente ese arreglo es pasado al DataGrid como su proveedor de datos (dataProvider).
Primero hacemos la llamada al método realizarConsulta del servicio SistemaGastos:
responder = new Responder(realizarConsultaResult, errorConsulta);
connection.call("SistemaGastos.realizarConsulta", responder, param1, param2, param3...);
Definimos el código de nuestra función realizarConsultaResult:
private function realizarConsultaResult(result:Array):void
{
if(result != null)
{
var totalRegistros:Number = result.length;
var totales_array:Array = new Array();
for (var i:int = 0; i < result.length; i++)
{
...
var divisa_txt:String = result[i].STRNOMBRE;
var simbolo_txt:String = result[i].STRSIMBOLO;
...
var cantidad:Number = result[i].NUMCANTIDAD;
if(totales_array.length == 0)
totales_array.push({cantidad:cantidad, divisaID:result[i].INTNUMDIVISA, divisa:divisa_txt, simbolo:result[i].STRSIMBOLO});
else
{
var posicion:Number = regresaPosicionMoneda(totales_array, result[i].INTNUMDIVISA);
if(posicion != -1)
{
var total:Number = totales_array[posicion].cantidad;
total += cantidad;
totales_array[posicion].cantidad = total;
}
else
totales_array.push({cantidad:cantidad, divisaID:result[i].INTNUMDIVISA, divisa:divisa_txt, simbolo:result[i].STRSIMBOLO});
}
data_provider.push({fecha_operacion:DateUtilities.returnReadableDate(result[i].DTMFECHA), concepto:result[i].STRCONCEPTO, cantidad:currencyFormatter.format(result[i].NUMCANTIDAD), cantidad_numero:result[i].NUMCANTIDAD, servicio:servicio_txt, divisa:divisa_txt, operacion:returnTipoOperacion(result[i].INTOPERACION)});
}
registros_dg.dataProvider = data_provider;
info_txt.text = "Total registros: " + totalRegistros + "\n";
for(var j:Number = 0; j < totales_array.length; j++)
{
var simbolo:String = totales_array[j].simbolo;
if(simbolo == "?")
simbolo = "€";
currencyFormatter.currencySymbol = simbolo;
info_txt.text += "\n --> " + totales_array[j].divisa + " = " + currencyFormatter.format(totales_array[j].cantidad);
}
}
else
info_txt.text = "";
CursorManager.removeBusyCursor();
}
Del código anterior podemos decir que… primero verificamos que el resultado obtenido por el servicio sea distinto de nulo (es decir, que no esté vacío) ya que si no nos mostraría error. En la variable totalRegistros almacenamos el número de registros obtenidos mediante la propiedad length del arreglo. Recorremos el arreglo result por medio de un ciclo for y dentro de este ciclo hacemos las operaciones que requerimos con los valores obtenidos. Observa que para acceder a cada una de las columnas de nuestros registros lo hacemos de la forma result[i].STRNOMBRE, result[i].STRSIMBOLO, result[i].INTNUMDIVISA, etc. donde STRNOMBRE, STRSIMBOLO, … son los nombres de nuestras columnas en la tabla tbloperacion de nuestra base de datos. Dentro del código de nuestro ciclo for estamos sumando (ingreso) o restando (si se trata de gasto) las cantidades pertenecientes a un mismo tipo de moneda; y así mostrar la cantidad ganada o gastada por tipo de moneda.

Dentro del archivo SistemaGastos.php observarás que el código de la función realizarConsulta es largo y está lleno de condicionales. Esto es porque las consultas las podemos realizar de acuerdo a los siguientes criterios:
Hemos llegado al final de este tutorial, no olvidemos revisar el ejemplo y bajar los archivos fuentes.






Hola que tal, tu proyecto esta muy bien explicado se te agradece que colabores y compartas tus experiencias. Una pregunta cuando se requiere publicar el proyecto de flex a la web, utilizo la misma tecnología de lenguaje servidor php con amfphp2.0 configuré private var gateway:String =”"; con la dirección del hosting que tengo al igual que la clase php la configuré con los datos del hosting
en algunos foros recomiendan cambiar el additional compiler arguments a:-use-network=false. Podrías brindar información o alguna fuente para realizar esta actividad. Gracias.
Hola, vi tu aplicacion, me parecio interesante, y me abrio los ojos, soy relativamente nuevo en Flex, empeze asi como tu con componentes y ocultandolos, pero se me hace mucho rollo, cuando son demasiados, asi ke opte por lo sig.
public var oPantalla:Object; // crear variable de tipo objeto
this.modulo hace referencia a un canvas en la aplicacion
modMenus es un componente basado en un panel, el cual contiene un grid y botones de agregar y modificar ke llaman a un title window, y tmb el boton de eliminar y el boton de cerrar, el de cerrar solo hace en el click=”removeModule()”
y lo quita de la pantalla, espero que te sirva, en la actualidad ya deje esa tecnica, ya que hace la aplicacion demasiado grande y estoy utilizando ahora el ModuleLoader, saludos.!!
public function createModule(pantalla:String):void
{
this.Modulo.removeAllChildren();
switch ( pantalla )
{
case “modMenus”:
oPantalla = new modMenus();
this.Modulo.addChild(oPantalla as modMenus);
break;
case “otro”:
}
}
public function removeModule():void
{
this.Modulo.removeAllChildren();
}
Hola, vi tu aplicacion, me parecio interesante, y me abrio los ojos, soy relativamente nuevo en Flex, empeze asi como tu con componentes y ocultandolos, pero se me hace mucho rollo, cuando son demasiados, asi ke opte por lo sig.
public function createModule(pantalla:String):void
{
this.Modulo.removeAllChildren();
switch ( pantalla )
{
case “modMenus”:
oPantalla = new modMenus();
this.Modulo.addChild(oPantalla as modMenus);
break;
Hola Carla, yo tengo un problema, y es con las fechas, como hago para poder navegar entre año, colocar el año de nacimiento es navegar por todos los años posteriores a ello y la verdad es tedioso.
entiendo que quizás no vaya relacionado con el tema, pero si la verdad no encuentro una respuesta.
gracias de antemano
cordial saludo, el ejemplo no carga. gracias de antemano.
DISCULPA EN TODOS LOS ACTIONSCRIPT ME MARCA QUE LA VARIABLE FUNCTION NO ES VALIDA, YO USO FLASHBUILDER 4 Y NECESITO SABER COMO RESOLVER ESOS ERRORES!!!! ES LA VERSION DE ACTIONSCRIPT O QUE? NECESITO SOLUCIONES PORFAVOR!!!!
OYE YA PUDE AGREGAR LA BASE DE DATOS, LO DEL AMF Y YA AGREGUE LOS ACHIVOS BASE EN MI FLEX, EL UNICO PROBLEMA QUE TENGO ES QUE USO FLASHBUILDER 4 Y ME MARCA ERRORES EN LOS ACTION SCRIT Y ME COMENTARON QUE PUEDE SER PORQUE USA OTRA VERSION DE FLASH, COMO PUEDO HACERLAS COMPATIBLES?
DISCULPEN PERO OSEA YA SE QUE LA CLASE PHP VA EN EL DIRECTORIO DE AMFPHP PERO LA CLASE SQL DONDE VA? A DONDE LA METO? NO ENTIENDO!!!! AYUDENME PLIS
Excelente artículo.
Apenas estoy enmpezando con Flex y particularmente necesito hacer cosas como estas, insertar, consultar, borrar, modificar registros de base de datos pero lo quería hacer con flex.
En otros tutoriales solo te muestran como recuperar información, no cómo ingresarla.
Muchas gracias por el aporte
Hola que tal, soy novato en el desarrollo con flex, me gustaria saber como poner una imagen a un panel, espero y me puedan ayudar, sin mas por el momento gracias
Que tal nuevamente saludando y agradeciendo tus aportes.
Una pregunta . . . estoy trabajando en una aplicacion con flex y air y la duda que tengo es una vez iniciado el proyecto como creo el ejecutable o que archivo del proyecto debo de ejecutar para abrir la aplicacion . . . .
Saludos…….
Excelente ejemplo, es un buen punto de partida. Qusiera hacerte una consulta:
1. Cual es la razon que en la funcion obtenerdivisas crees un array para retornar el resultado, en vez de retornarlo directamente.
2. Seria genial que pudieras poner un ejemplo para poder registrar un documento cabecera – detalle con varias lineas de detalle.
Muy agraecido por el tiempo dedicado.
Que tal buenas tardes
Antes que nada felicitarte por tu trabajo y por supuesto agradecerte por compartirlo.
Necesito hacerte unas preguntas con relacion a flex y al manejo de datos a traves de un arrayColletion con php con datos de MYSQL
Me podrias apoyar ??
Si es asi me puedes proporcionar tu direccion de correo por fvor
SAludos desde Hidalgo
Excelente Carla…
Mil gracias por compartir.
Me pueden explicar paso a paso como hecho a andar este proyecto, apenas empiezo a conocer php, mysql y flex
Antonio y podrias mostrarnos tu cual es la mejor manera de hacerlo y un ejemplo de como hacerlo?