viernes, 20 de febrero de 2009

FlexLib Librería - Open Source con componentes para Flex


Todos conocemos la importancia que tienen los componentes a la hora de realizar un diseño web (viene siendo uno de los aportes mas importantes de Flex sobre Flash). La comunidad desarrolladora de Flex se unó para realizar una librería llamada FlexLib con los mejores componentes de los mejores programadores de flex conocidos. Entre algunos de los componentes (solo con los que yo he trabajado) que se incluyeron se encuentran:

  1. Horizontal Accordion: Es similar al Vertical Accordion del framework original de flex pero permite deslizar los contenedores de izquierda a derecha en lugar de arriba hacia abajo.
  2. DragScrollingCanvas: Algo parecido a lo que sucede en google maps, podemos arrastrar el contenido interno del canvas sin la necesidad de usar un ScrollBar o la ruedad del mouse, solo haciendo un drag sobre el contenido.
  3. ButtonScrollingCanvas: Coloca botones en los extremos del contenedor, al presionarlos, colocar el mouse sobre ellos, etc. Se realiza un Scroll del contenido.
  4. CanvasButton: Es un contenedor canvas que hereda de la clase Button, por lo que permite ser utilizado como HeatherRenderer de un Horizonal o Vertical Acorrdion.
En Fin, si quieren conocer todos los componentes que tiene la librería los invito a entrar a la siguiente direccion de FlexLib en Google Codes. De todas maneras les dejo un vinculo para descargar la librería.


Continuar leyendo...

lunes, 16 de febrero de 2009

Utilizando un ItemEditor para modificar las filas de un DataGrid


Este post lo voy a realizar como una continuacion del post anterior Utilizando un ItemRender en un DataGrid pero en este caso vamos a ampliar el ejemplo utilizando una propiedad llamada itemEditor.

Un itemEditor es un componente que se utiliza para modificar informacion, a diferencia de un itermRenderer (que se utiliza unicamente para mostrar informacion) el itemEditor es ideal para realizar modificaciones a datos utilizando los componentes mas idoneos para facilitar la modificacion. La mejor manera de que se vea lo que trato de decir es mostrando el ejemplo que vamos a realizar:
Como pueden notar al hacer click sobre alguna de las celdas los datos que se visualizaban ahora se renderizan de otra manera ideal para su manipulacion.

Para lograr esto debemos colocar en cada columna una propiedad llamada itemEditor que contendrá la ruta hacia la clase o componente que queremos utilizar para renderizar los datos en el momento de la modificacion de la celda. En el caso de este ejemplo la estructura del DataGrid queradía de la siguiente manera.

<mx:DataGrid id="dg" x="10" y="10" columnWidth="20" dataProvider="{datos}" rowHeight="25" editable="true" width="357">
<mx:columns>
<mx:DataGridColumn headerText="RadioButton" dataField="radioValue" editable="false" itemRenderer="mx.controls.RadioButton"/>
<mx:DataGridColumn headerText="NumericStepper" dataField="numeriValue" editorDataField="value" itemEditor="mx.controls.NumericStepper"/>
<mx:DataGridColumn headerText="Image" dataField="url" itemRenderer="mx.controls.Image" itemEditor="mx.controls.TextInput"/>
<mx:DataGridColumn headerText="CheckBox" dataField="checkBoxValue" editable="false" itemRenderer="mx.controls.CheckBox"/>
<mx:DataGridColumn headerText="TextInput" dataField="textValue" editable="true" itemEditor="mx.controls.TextInput"/>
</mx:columns>
</mx:DataGrid>

Si no queremos que una columna en especifica se pueda editar debemos asignarle la propiedad editable=false.

Bueno esto es todo en este ejemplo, como pueden ver es muy facil utilizar estos itemEditors para modificar las filas del DataGrid. Cualquier duda no duden en escribirme al correo aalejo@gmail.com.


Continuar leyendo...

domingo, 15 de febrero de 2009

Utilizando un ItemRender en un DataGrid

En este ejemplo vamos llenar un DataGrid como lo hacemos normalmente, es decir, a través de su propiedad dataProvider. Lo que tendrá de particular será la manera como mostraremos la informacion dentro del DataGrid. Los datos seran mostrados de 5 maneras diferentes (Una manera por cada columna) utilizando la propiedad ItemRenderer de las columnas del DataGrid (DatagridColumns).

Los ItemRenderer que utilizaremos para mostrar la información serán los siguientes: RadioButton, NumericStepper, Image, CheckBox, TextInput. Si quieres saber como utilizar un ItermRender propio y mas complejo te recomiendo el tutorial Datagrid con un ItemRenderer Propio (Personalizado).

Bueno para empezar vamos a crear un nuevo proyecto en flex llamado DataGridSimpleItemrenderer, dentro de esta aplicacion vamos a agregar un nuevo Datagrid que va a contener 5 columnas, a cada una de las columnas le vamos a colocar la propiedad itemRenderer con la ruta a la clase de flex que vamos a utilizar para renderizar, por ejemplo, si se trata de un RadioButton debemos colocar mx.controls.RadioButton. Tambien debemos colocar la propiedad dataField con el nombre del atributo que va a contener la data que va a utilizar ese RadioButton para renderizarse, recuerden que el DataGrid se va a llenar con un arreglo de objetos (cada objeto va a tener un atributo que se va a user para renderizar una columna, ese atributo será el que colocaremos como dataField segun sea el caso). Por ultimo llenamos la pripiedad headerText con el mobre que le queremos dar a esa columna.

Una vez creado el DataGrid y colocados estos atributos en las columnas del DataGrid tendremos el siguiente codigo:

<mx:DataGrid id="dg" x="10" y="10" dataProvider="{datos}" rowHeight="25" width="288">
<mx:columns>
<mx:DataGridColumn headerText="RadioButton" dataField="radioValue" itemRenderer="mx.controls.RadioButton"/>
<mx:DataGridColumn headerText="NumericStepper" dataField="numeriValue" itemRenderer="mx.controls.NumericStepper"/>
<mx:DataGridColumn headerText="Image" dataField="url" itemRenderer="mx.controls.Image"/>
<mx:DataGridColumn headerText="CheckBox" dataField="checkBoxValue" itemRenderer="mx.controls.CheckBox"/>
<mx:DataGridColumn headerText="TextInput" dataField="textValue" itemRenderer="mx.controls.TextInput"/>
</mx:columns>
</mx:DataGrid>

Ahora debemos crear una funcion llamada "init" que llamaremos cuando ocurra el evento CreationComplete de la aplicación, esta función se encargara de llenar el arreglo dataProvider que contendrá el DataGrid, recuerden que los objetos que le vamos a insertar a este dataProvider deben tener atributos iguales a los que mencionamos como dataField en las columnas del DataGrid. Al final tendremos todo el codigo de la aplicación como lo siguiente:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()">
<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.collections.ArrayCollection;

[Bindable] public var datos : ArrayCollection = new ArrayCollection();

public function init() : void
{
var obj : Object = new Object();
obj.checkBoxValue = true;
obj.radioValue = false;
obj.numeriValue = 7;
obj.textValue = "Hola Mundo";
obj.url = "http://aalejo.googlepages.com/idea.jpg";

datos.addItem(obj);
}
]]>
</mx:Script>
<mx:DataGrid id="dg" x="10" y="10" dataProvider="{datos}" rowHeight="25" width="288">
<mx:columns>
<mx:DataGridColumn headerText="RadioButton" dataField="radioValue" itemRenderer="mx.controls.RadioButton"/>
<mx:DataGridColumn headerText="NumericStepper" dataField="numeriValue" itemRenderer="mx.controls.NumericStepper"/>
<mx:DataGridColumn headerText="Image" dataField="url" itemRenderer="mx.controls.Image"/>
<mx:DataGridColumn headerText="CheckBox" dataField="checkBoxValue" itemRenderer="mx.controls.CheckBox"/>
<mx:DataGridColumn headerText="TextInput" dataField="textValue" itemRenderer="mx.controls.TextInput"/>
</mx:columns>
</mx:DataGrid>
</mx:Application>

Bueno esto es todo, de todas maneras aqui les dejo el codigo de la aplicacion para que lo puedan descargar, y tambien pueden ver el ejemplo en funcionamiento. Cualquier duda me pueden escribir a mi email aalejo@gmail.com.



Continuar leyendo...

sábado, 14 de febrero de 2009

Carrito de compras con PHP (Orientado a Objetos)


En este tutorial vamos a crear y utilizar las clases necesarias para manejar un carrito de compras en PHP. Las funciones que esta clase "Carrito" tendrá serán:
  • Agregar un producto.
  • Eliminar un producto.
  • Modificar un producto.
  • Imprimir el carrito.
Por fines didácticos quiero utilizar el patrón de diseño Iterator en esta clase "Carrito", ese patrón de diseño nos permitie recorrer los arreglos, collecciones, etc. Sin exponer sus estructuras, es decir, vamos a estandarizar la manejar de recorrer ese tipo de objetos, siempre que queramos recorrer un arreglo, coleccion, etc. Vamos a hacerlo de la misma manera sin importar que clase es.

Tambien vamos a trabajar el carrito creado como una variable de sesion, para que este presente durante toda la aplicación (mientras el usuario realiza sus compras).

Creando la clase Producto

Esta clase se encargará de guardar la informacion relativa a cada producto en especifibo, cada que que queramos un nuevo producto debemos crear una instancia de esta clase y pasarle sus atributos: codigo (para la identificacion unica), cantidad, precio, nombre, etc.

Archivo Producto.php

class Producto
{
var $codigo;
var $nombre;
var $cantidad;
var $precio;
}

Creando la clase Carrito

La clase Carrito no es mas que una collección de objetos Producto y los metodos para manejar esos productos:

Archivo Carrito.php

/**
* Esta clase permite manejar funciones basicas de un carrito de
* compras, esta desarrollada usando el patron de diseño iterator
* para orfecer mayor encapsulamiento y orden en los datos
* para utilizarla deben tener una clase producto que contenga
* el atributo "codigo" representando la identificacion del producto
*
* Cualquier duda pueden escribir a aalejo@gmail.com
* o entrar en la explicacion de esta clase en
* www.internetdeveloping.blogspot.com
*/
require('Collection.php');
require('Producto.php');

Class Carrito
{
/**
* Viene representando la lista de productos del carrito
* @var Collection
*/
var $productos;

function Carrito()
{
$this->productos = new Collection('Producto');
}

/**
* Esta funcion devuelve un producto a partir del codigo
* @param $codigo
* @return Producto
*/
public function getProducto($codigo)
{
$iterator = $this->productos->getIterator();
while($iterator->valid())
{
$prod = $iterator->current();

if($prod->codigo == $codigo)
{
return $prod;
}

$iterator->next();
}

return null;
}

/**
* Permite agregar un producto al carrito de compras
* @param $producto
* @return null
*/
public function agregarProducto($producto)
{
$prod = $this->getProducto($producto->codigo);
if($prod) $prod->cantidad++;
else
$this->productos->add($producto);
}

/**
* Elimina un producto del carrito, a partir de un codigo
* @param $codigo
* @return null
*/
public function eliminarProducto($codigo)
{
$existe = false;
$iterator = $this->productos->getIterator();
while($iterator->valid())
{
$prod = $iterator->current();
if($prod->codigo == $codigo)
{
if($prod->cantidad > 1)
$prod->cantidad--;
else
{
$this->productos->remove($iterator->key());
}
}
$iterator->next();
}
}

/**
* Se utiliza solo con fines de desarrollo, imprime la lista
* de productos del carrito, se puede modificar para imprimir
* toda la informacion de cada producto
* @return String
*/
public function trace()
{
$out = "";
$iterator = $this->productos->getIterator();
while($iterator->valid())
{
$prod = $iterator->current();

$out .= "Producto ".$prod->nombre." -> ".$prod->cantidad."
";

$iterator->next();
}

return $out;
}

}

Seguramente se preguntan porque la variables $productos es de tipo Collection, bueno esto es muy simple, para no trabajar con arreglos tediosos decidi utilizar una clase que encontre en internet llamada Collection.php, lo bueno de esta clase es que implementa la interaz IteratorAggregate que nos permitirá utilizar el patron de diseño Iterator que explique anteriormente, por otro lado, sus metodos son mucho mas intuitivos que los de un arreglo. A continuación les dejo el codigo de la clase Collection.php

Archivo Collection.php

class Collection implements Countable, IteratorAggregate, ArrayAccess
{
protected $_valueType;
protected $_collection = array();

/**
* Construct a new typed collection
* @param string valueType collection value type
*/
public function __construct($valueType)
{
$this->_valueType = $valueType;
}

/**
* Add a value into the collection
* @param string $value
* @throws InvalidArgumentException when wrong type
*/
public function add($value)
{
if(!$this->isValidType($value))
throw new InvalidArgumentException('Trying to add a value of wrong type');

$this->_collection[] = $value;
}

/**
* Set index's value
* @param integer $index
* @param mixed $value
* @throws OutOfRangeException
* @throws InvalidArgumentException
*/
public function set($index, $value)
{
if($index >= $this->count())
throw new OutOfRangeException('Index out of range');

if(!$this->isValidType($value))
throw new InvalidArgumentException('Trying to add a value of wrong type');

$this->_collection[$index] = $value;
}

/**
* Remove a value from the collection
* @param integer $index index to remove
* @throws OutOfRangeException if index is out of range
*/
public function remove($index)
{
if($index >= $this->count())
throw new OutOfRangeException('Index out of range');

array_splice($this->_collection, $index, 1);
}

/**
* Return value at index
* @param integer $index
* @return mixed
* @throws OutOfRangeException
*/
public function get($index)
{
if($index >= $this->count())
throw new OutOfRangeException('Index out of range');

return $this->_collection[$index];
}

/**
* Determine if index exists
* @param integer $index
* @return boolean
*/
public function exists($index)
{
if($index >= $this->count())
return false;

return true;
}
/**
* Return count of items in collection
* Implements countable
* @return integer
*/
public function count()
{
return count($this->_collection);
}

/**
* Determine if this value can be added to this collection
* @param string $value
* @return boolean
*/
public function isValidType($value)
{
$baseType = gettype($value);
if($this->_valueType == $baseType)
return true;

if($baseType == 'object')
{
$class = get_class($value);

if($this->_valueType == $class)
return true;
}

return false;
}

/**
* Return an iterator
* Implements IteratorAggregate
* @return ArrayIterator
*/
public function getIterator()
{
return new ArrayIterator($this->_collection);
}

/**
* Set offset to value
* Implements ArrayAccess
* @see set
* @param integer $offset
* @param mixed $value
*/
public function offsetSet($offset, $value)
{
$this->set($offset, $value);
}

/**
* Unset offset
* Implements ArrayAccess
* @see remove
* @param integer $offset
*/
public function offsetUnset($offset)
{
$this->remove($offset);
}

/**
* get an offset's value
* Implements ArrayAccess
* @see get
* @param integer $offset
* @return mixed
*/
public function offsetGet($offset)
{
return $this->get($offset);
}

/**
* Determine if offset exists
* Implements ArrayAccess
* @see exists
* @param integer $offset
* @return boolean
*/
public function offsetExists($offset)
{
return $this->exists($offset);
}
}


Utilizando del carrito de compras


Bueno ya estamos listos, ahora solo falta probar nuestra clase, para eso vamos a crear un arrchivo llamado VistaCarrito.php donde vamos a crear una instancia de la clase Carrito y vamos a agregarle, eliminarle y modiicarle produtos.

Archivo VistaCarrito.php

session_start();
require("Carrito.php");

$carrito = new Carrito();
$_SESSION["carrito"] = $carrito;
//Creo dos productos para jugar con ellos
$prod = new Producto();
$prod->codigo = 1;
$prod->cantidad = 1;
$prod->nombre = "Cama";
$prod->precio = 50;

$prod2 = new Producto();
$prod2->codigo = 2;
$prod2->cantidad = 1;
$prod2->nombre = "Silla";
$prod2->precio = 23;

echo "Agregando dos productos...
";
$_SESSION["carrito"]->agregarProducto($prod);
$_SESSION["carrito"]->agregarProducto($prod2);

echo $_SESSION["carrito"]->trace();

echo "Modificando el producto de codigo 1...
";
$auxProd = $_SESSION["carrito"]->getProducto("1");
$auxProd->cantidad++;

echo $_SESSION["carrito"]->trace();

echo "Eliminando el producto de codigo 2...
";
$_SESSION["carrito"]->eliminarProducto("2");

echo $_SESSION["carrito"]->trace();

echo "Listo...";

Recuerden que antes de empezar a utilizar las variables de session siempre debemos llamar a la funcion session_start(). Creamos la variable carrito como un objeto de sesion para poder utilizarlo durante toda la paigna web.

Bueno espero que todo haya quedado claro, aqui les dejo el codigo fuente de la clase para los que la quieran utilizar, cualquier duda me pueden escribir a mi correro aalejo@gmail.com.





Continuar leyendo...

jueves, 5 de febrero de 2009

Datagrid con un ItemRenderer Propio (Personalizado)


En esta oportunidad vamos a colocar un ItemRenderer personalizado dentro de un DataGrid y que sin embargo los datos se sigan pasando de manera automatica a través del dataSource y utilizando Binding.

El ItemRenderer que vamos a utilizar va a ser un componente flex creado completamente aparte y a nuestro gusto, y le vamos a pasar sus datos a renderizar a través de la propiedad Data que tienen todos los objetos que implementan la interfaz IDataRender.

Para empezar vamos a entender un poco sobre la interfaz IDataRenderer, cualquier objeto que la implemente contiene una propiedad llamada data, por donde recibe toda la información a renderizar (a pintar, graficar, como lo quieran llamar), la única informacion que un objeto de este tipo deberia recibir a través de otras propiedades seria la concerniente a su comportamiento.

En nuestro ejemplo la informacion a renderizar son dos datos:
  • url: Contiene la direccion donde se encuentra alojada la fotografia del producto.
  • nombre: El nombre del objeto.
El ItemRenderer por defecto en el DataGrid es el componente "Label", aunque a veces no nos damos cuenta. Si queremos cambiar el itemrenderer de una columna solo debemos setearle la propiedad "itemrenderer" igual a la dirección donde se encuentra nuestra clase (o componente), esta dirección debe incluir la ruta desde la raiz del proyecto. Una vez hecho esto el itemrenderer recibirá la información a través de la propiedad "data" de la que hablamos anteriormente.

Bueno basta de hablar tanto y vamos a empezar. Creamos el componente de flex que va a renderizar los datos:
Archivo pictureRender.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="112" height="126">
<mx:Script>
<![CDATA[

[Bindable]
public var nombre : String = "Ramon";
[Bindable]
public var url : String = "assets/1.jpg";

public override function set data(value:Object):void
{
nombre = value.data.nombre;
url = value.data.url;

super.data = value;
}
public override function get data():Object
{
return super.data;
}
]]>
</mx:Script>
<mx:Image x="10" y="10" width="89" height="89" source="{url}"/>
<mx:Label x="10" y="104" width="92" text="{nombre}"/>
</mx:Canvas>

El componente lo hemos creado a partir de la clase Canvas, como esta clase implementa la interfaz IDataRenderer, sobreescribi los metodos get y set de la propiedad data para aplicarle una logica especial a estos datos, ya que debemos extraer el campo "nombre" y "url" de este objeto para colocarlo en las respectivas variables del componente, estas variables deben ser declaradas como [Binding] en el momento de su definición, ya que de esta manera los datos que ellas contengan serán transferidos al objeto Image (en el caso de la url) y Label (en el caso del nombre) sin nosotros tener que ocuparnos de eso, es decir, en el momento en que cambie la variable url entonces cambiara el valor de la propiedad "source" del objeto Image. Para aprender un poco mas sobre el metatag [Binding] puede hacer click aqui.

Ahora procedemos a llenar el archivo de la aplicacion:
Archivo DatagridDataRender.mxml


<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()">
<mx:Script>
<![CDATA[
import mx.core.IDataRenderer;
import mx.collections.ArrayCollection;

[Bindable]
public var renderData : ArrayCollection = new ArrayCollection( [
{url:'assets/1.png', nombre:'Monitor Daslex'},
{url:'assets/2.png', nombre:'Maletin Ladrix'}]);
[Bindable]
public var datosDG : ArrayCollection = new ArrayCollection();
public function init() : void
{
var obj1 : Object = new Object();
obj1.data = renderData.getItemAt(0);
obj1.descripcion = "Producto de buena calidad.";
var obj2 : Object = new Object();
obj2.data = renderData.getItemAt(1);
obj2.descripcion = "Producto de mala calidad.";

datosDG.addItem(obj1);
datosDG.addItem(obj2);
}
]]>
</mx:Script>
<mx:DataGrid x="10" y="10" dataProvider="{datosDG}" rowHeight="130" width="373" rowCount="2">
<mx:columns>
<mx:DataGridColumn headerText="Producto" dataField="data" itemRenderer="pictureRender" />
<mx:DataGridColumn headerText="Descripcion" dataField="descripcion"/>
</mx:columns>
</mx:DataGrid>

</mx:Application>
Este archivo tiene dos partes fundamentales, en la primera seccion definimos dos ArrayCollection llamados "renderData" y "datosDG". El primero de ellos contiene los objetos "data" que se van a utilizar para enviar informacion a los ItemRenderer, el segundo de ellos va a ser sincronizado con la propiedad dataProvider del DataGrid (contendrá una lista donde cada item será equivalente a una fila en el datagrid.)

La función init, que se llama cuando se dispara el evento creationComplete de la aplicación, se encarga de llenar el ArrayCollection datosDG, especificamente crea objetos con dos variables principales "data" y "descripcion", estas variables deben ser colocadas como "dataFields" en las columnas del DataGrid para que el DataGrid haga la carga de datos automaticamente. Si se fijan, la pripiedad data es llenada con la información a renderizar por el ItemRederer.

Bueno ya hemos finalizado nuestra aplicación, cualquier duda me pueden escribir a mi correo aalejo@gmail.com, si todo los hicieron correctamente su aplicacion debería verse de la siguiente manera:



Continuar leyendo...

Obtener una celda o dato de un Datagid


Un breve ejemplo de como obtener los datos de un datagrid por medio de su propiedad dataProvider.

En el ejemplo vamos a usar un ArrayCollection para llenar un Datagrid, luego vamos a obtener la fila deseada del Datagrid segun el "numero de fila". Tambien vamos a ser capaces de detectar cual es la fila que esta seleccionada y obtener su informacion con la propierar selectedIndex de la clase Datagrid.

Para empezar vamos a crear una clase Persona con los datos: Nombre, Apellido y cedula:

Archivo clase Persona.as


package com
{
public class Persona
{
public var nombre : String;
public var apellido : String;
public var cedula : String;


public function Persona()
{
}

}
}


Luego debemos en la aplicacion principal crear una funcion handler llamada "init" para el evento creationComplete, este evento se dispara apenas la aplicacion se acaba de terminar de crear y esta lista para empezar a ejecutarse, creamos un ArrayCollection y agregarle por ejemplo, dos personas cuales quiera:

public function init() : void
{
var persona1 : Persona = new Persona();
persona1.nombre = "Alejandro";
persona1.apellido = "Sanchez";
persona1.cedula = "17405689";

var persona2 : Persona = new Persona();
persona2.nombre = "Ramon";
persona2.apellido = "Ramos";
persona2.cedula = "14758243";

personas.addItem(persona1);
personas.addItem(persona2);
}

Recuerden colocar esta funcion como handler del evento creationComplete de la aplicacion.

Ahora debemos decirle al Datagrid que debe llenarse con la informacion de ese ArrayCollection, para eso utilizamos la propiedad dataProvider del Datagrid, ademas debemos decirle como manejar esa informacion, debemos crear tres columnas (DataGridColumn) dandole valores a las propiedades headerText (el nombre que queremos mostrar en el tope de esa columna) y dataField (el nombre de la propiedad de la clase persona que contiene el dato a mostrar):

<mx:DataGrid id="dg" x="10" y="10" dataProvider="{personas}">
<mx:columns>
<mx:DataGridColumn headerText="Nombre" dataField="nombre"/>
<mx:DataGridColumn headerText="Apellido" dataField="apellido"/>
<mx:DataGridColumn headerText="Cedula" dataField="cedula"/>
</mx:columns>
</mx:DataGrid>

Ahora procedemos a crear un pequeño formulario donde se van a mostrar los datos que vamos a obtener de la fila que se introduzca. Para eso vmos a crea el siguente fomulario:

<mx:TextInput id="nro" x="10" y="195" width="41"/>
<mx:Button x="59" y="195" label="Obtener" click="obtener()"/>
<mx:Label x="10" y="169" text="¿Qué fila deseas obtener?"/>
<mx:Canvas x="10" y="225" width="287" height="96" backgroundColor="#FFFFFF">
<mx:Label x="10" y="10" text="Nombre"/>
<mx:Label x="10" y="36" text="Apellido"/>
<mx:Label x="10" y="62" text="Cédula"/>
<mx:Label id="labelNombre" x="62" y="10" text="No definido"/>
<mx:Label id="labelApellido" x="62" y="36" text="No definido"/>
<mx:Label id="labelCedula" x="62" y="62" text="No definido"/>
</mx:Canvas>
<mx:Button x="140" y="195" label="La fila seleccionada" width="157" click="filaSeleccionada()"/>

Si se fijan hay dos cosas importantes aqui: La llamada a los metodos obtener() y filaSeleccionada() cuando el usuario haga click en cada uno de los botones, y que hemos identificado los controles que vamos a trabajar desde el codigo Action Script asignandoles una identificado String en la propiedad "id". Ahora debemos definir que van a realizar los metodos handlers que hemos colocado para manejar los eventos click.

public function obtener() : void
{
if(personas.length>Number(nro.text) && Number(nro.text)>=0)
{
var auxPersona : Persona = Persona(personas.getItemAt(Number(nro.text)));

labelApellido.text = auxPersona.apellido;
labelCedula.text = auxPersona.cedula;
labelNombre.text = auxPersona.nombre;
}
else
{
Alert.show('El indice excede el numero de filas o es negativo, recuerda que se empieza por el numero 0');
}

}

public function filaSeleccionada() : void
{
if(dg.selectedItem) nro.text = dg.selectedIndex.toString();
else Alert.show("Debes seleccionar un item");
}

El primer metodo (obtener) se encarga de obtener los datos del numero de fila que contenga el textInput llamado "nro". El segundo metodo obtiene el indice de la fila seleccionada (Si hay alguna, por supuesto).

Bueno al final el codigo de nuestro archivo aplicacion llamado ejemploDataGrid.mxml queda de la siguiente manera:
Archivo clase ejemploDatagrid.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()">
<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.collections.ArrayCollection;
import com.Persona;

[Bindable]
public var personas : ArrayCollection = new ArrayCollection();

public function init() : void
{
var persona1 : Persona = new Persona();
persona1.nombre = "Alejandro";
persona1.apellido = "Sanchez";
persona1.cedula = "17405689";

var persona2 : Persona = new Persona();
persona2.nombre = "Ramon";
persona2.apellido = "Ramos";
persona2.cedula = "14758243";

personas.addItem(persona1);
personas.addItem(persona2);
}

public function obtener() : void
{
if(personas.length>Number(nro.text) && Number(nro.text)>=0)
{
var auxPersona : Persona = Persona(personas.getItemAt(Number(nro.text)));

labelApellido.text = auxPersona.apellido;
labelCedula.text = auxPersona.cedula;
labelNombre.text = auxPersona.nombre;
}
else
{
Alert.show('El indice excede el numero de filas o es negativo, recuerda que se empieza por el numero 0');
}

}

public function filaSeleccionada() : void
{
if(dg.selectedItem) nro.text = dg.selectedIndex.toString();
else Alert.show("Debes seleccionar un item");
}
]]>
</mx:Script>
<mx:DataGrid id="dg" x="10" y="10" dataProvider="{personas}">
<mx:columns>
<mx:DataGridColumn headerText="Nombre" dataField="nombre"/>
<mx:DataGridColumn headerText="Apellido" dataField="apellido"/>
<mx:DataGridColumn headerText="Cedula" dataField="cedula"/>
</mx:columns>
</mx:DataGrid>
<mx:TextInput id="nro" x="10" y="195" width="41"/>
<mx:Button x="59" y="195" label="Obtener" click="obtener()"/>
<mx:Label x="10" y="169" text="¿Qué fila deseas obtener?"/>
<mx:Canvas x="10" y="225" width="287" height="96" backgroundColor="#FFFFFF">
<mx:Label x="10" y="10" text="Nombre"/>
<mx:Label x="10" y="36" text="Apellido"/>
<mx:Label x="10" y="62" text="Cédula"/>
<mx:Label id="labelNombre" x="62" y="10" text="No definido"/>
<mx:Label id="labelApellido" x="62" y="36" text="No definido"/>
<mx:Label id="labelCedula" x="62" y="62" text="No definido"/>
</mx:Canvas>
<mx:Button x="140" y="195" label="La fila seleccionada" width="157" click="filaSeleccionada()"/>
</mx:Application>

El ejemplo en funcionamiento luce asi:




Continuar leyendo...

lunes, 2 de febrero de 2009

Video tutorial Creacion de un componente Flex


En este tutorial voy a explicar los principios básicos a seguir para la creación de un componete flex. Para lograr esto vamos a crear un componente que cotenga un rectangulo que se mueva. Cada vez que este componente sea agregado a una aplicacion flex, entonces la aplciación tendrá un cuadrado que se mueva hacia la derecha. Por mas inutil que resulte este componente, me parece perfecto ejemplo didáctico. De todas maneras próximamente prometo crear componentes mas utiles y explicar como los hice.

Para empezar el tutorial debemos entender los conceptos básicos que estan presentes en todos los componentes flex, es decir, en todas las clases de flex que heredan de la clase UIComponent (que es el componente mas básico).

Parte I

Parte II


La clase UIComponente propone 5 métodos fundamentales a utilizar:
  1. contructor: Este metodo se llama de primero en todos los componentes flex, lleva el mismo nombre que la clase que se esta creando. Permite inicializar variables entre otras coas.
  2. createChildren: Este metodo se utiliza para agregar hijos al componente, todos los componentes tienen una lista de hijos dentro de si, todos estos hijos son actualizados por otros métodos como updateDisplayList, para que un objeto pueda ser hijo de un componente debe heredar de la clase DisplayObject. Este metodo solo se ejecuta una vez al inicializarse el componente. Los hijos que se quieran agregar después deben agregarse en otros segmentos de código.
  3. commitProperties: Este metodo se invoca para definir o actualizar las propiedades del componente. Se puede ejecutar varias veces, y siempre se ejecuta la rimera vez justo despues del metodo createChildren.
  4. measure: Este metodo se utiliza para definir el tamaño del componente, cada vez que flex no sabe que tamaño tiene el componente entra en este método. Es importante saber que si se define un tamaño explicito (width y height) al componente entonces flex no entrará mas en este método.
  5. updateDisplayList: Este método se encarga de pintar el componente, en este lugar se ejecutan todas las sentencias de coódigo que tengan que ver con la visualizacion del componente. Tambien se ejecuta muchas veces.
Cuando un componente es creado por primera vez por flex todos estos metodos se ejecutan en el siguiente orden:



Es importante saber que estos tres últimos métodos mencionados no se pueden invocar directamente, si queremos llamarlos debemos utilizar unas funciones pensadas para eso:

La razón de no llamar a estos métodos directamente es porque el framewor de flex debe llamarlos cuando lo considere pertinente, es decir, con los métodos invalidateProperties, invalidateSize e invalidateDisplayList del LayoutManager nosotros le decimos a flesx que porfavor los llame, a partir de ese momento flex esperará el momento adecuado para llamarlos. Esto debemos tenerlo siempre en cuanta para que no valla a darnos problemas en un futuro.

Creando nuestro componente.

Recuerden que el componente que vamos a crear se encargará de dibujar un rectangulo e irlo moviendo hacia la derecha. Para crear el componente vamos a agregar una nueva actionScript Class que llamaremos MyFirstComponent.as donde colocaremos el siguiente código:
Código MyFirstComponent.as

package com.view
{
import flash.events.TimerEvent;
import flash.utils.Timer;

import mx.core.UIComponent;
import mx.skins.ProgrammaticSkin;


public class MyFirstComponent extends UIComponent
{
/*
Defino las variables que voy a utilizar en este componente.
*/
private var bloque : ProgrammaticSkin;
private var _timer : Timer = new Timer(50);
private var procesarBloque : Boolean = false;

public function MyFirstComponent()
{
super();
trace("constructor");
/*
Empiezo el timer y defino un handler llamado pintar para el evento
TimerEvent.TIMER que es disparado por el timer cada 50 miliseg.

*/
_timer.addEventListener(TimerEvent.TIMER,pintar);
_timer.start();
}

override protected function createChildren() : void
{
super.createChildren();
/*
Creo un ProgrammaticSkin y lo agrego a la lista de hijos del
componente.
*/
bloque = new ProgrammaticSkin();
this.addChild(bloque);

trace("createChildren");
}

/*
Utilizamos este metodo para cambiar el tamaño de nuestro componente
*/
override protected function measure() : void
{
super.measure();
trace("measure");

measuredWidth += 30;
measuredHeight += 30;

}


override protected function commitProperties() : void
{
super.commitProperties();
trace("commitProperties");

/*
Debemos asegurarnos de que "bloque" este creado.
*/
if(bloque)
{
//Solo queremos aumentar la coordenada x de "bloque" si es menor a 100.
if(bloque.x<100) procesarBloque = true;
else procesarBloque = false;

/*
Esta condicional nos permite restringir los bloques que deseamos que sean procesados
esto se puede usar para mejorar el rendimiento del componente ya que de esta manera
podemos controlar la ejecucion de las porciones de codigo, no se debe ejecutar la
porcion que no sea necesaria.
*/
if(procesarBloque)
{
bloque.x++;
}
}

}


override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number) : void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);

trace("updateDisplayList");

/*
En este punto pintamos nuestro bloque con el el ancho y alto de 200.
es importante dear de pintar a "bloque si no han ocurrido modificaciones de sus propiedads,
para eso usamos la bandera "procesarBloque" que tambien la utilizamos en el commmitproperties
*/
if(procesarBloque)
{
//bloque.graphics.clear();
bloque.graphics.lineStyle(1,0x000000,1);
bloque.graphics.beginFill(Math.random() * 20000000, 1);
bloque.graphics.moveTo(unscaledWidth,unscaledHeight);
bloque.graphics.lineTo(unscaledWidth,0);
bloque.graphics.lineTo(0,0);
bloque.graphics.lineTo(0,unscaledHeight);
}


}

/*
Esta funcion se llama cada 50 milisegundos.
*/
public function pintar(e : TimerEvent) : void
{
//Le digo a flex que cuando considere pertinente puede hacer
//un llamado a la funcion "commitProperties".
this.invalidateProperties();
this.invalidateDisplayList();
this.invalidateSize();


}
}
}


Las variable bloque será donde pintaremos todo nuestro cuadrado, un ProgrammaticSkin es una clase muy liviana que permite utilizar shapes sobre ellas, es decir, pintar lineas y rellenarlas.

La variable _timer se encargará de hacer una llamada a las funciones invalidateProperties, invalidateSize e invalidateDisplayList cada 50 milisegundos para que el cuadrado pintado se refresque visualmente y para que se le modifique su tamaño y propiedades.

La variable procesarBloque se definió para restringir los bloques de codigo que seran ejecutados cada vez que se llame al método commitproperties y updateDisplayList (ya que como estos métodos los vamos a llamar frecuentemente, es mejor que no se ejecute todo el codigo que ellos contienen constantemente.).

En el método create children debemos instanciar y agregar el ProgrammaticSkin que definimos ("bloque") al componente (para que pueda ser visualizado).

override protected function createChildren() : void
{
super.createChildren();
/*
Creo un ProgrammaticSkin y lo agrego a la lista de hijos del
componente.
*/
bloque = new ProgrammaticSkin();
this.addChild(bloque);

trace("createChildren");
}

En el método commitProperties se encargará de aumentar la propiedad "x" de la variable "bloque" para que el cuadradro que esta represente se mueva hacia la derecha.

override protected function commitProperties() : void
{
super.commitProperties();
trace("commitProperties");

/*
Debemos asegurarnos de que "bloque" este creado.
*/
if(bloque)
{
//Solo queremos aumentar la coordenada x de "bloque" si es menor a 100.
if(bloque.x<100) procesarBloque = true;
else procesarBloque = false;

/*
Esta condicional nos permite restringir los bloques que deseamos que sean procesados
esto se puede usar para mejorar el rendimiento del componente ya que de esta manera
podemos controlar la ejecucion de las porciones de codigo, no se debe ejecutar la
porcion que no sea necesaria.
*/
if(procesarBloque)
{
bloque.x++;
}
}

}

El metodo updateDisplayList se encargara de volver a pintar el cuadrado ahora que esta un podo mas a la derecha (porque commitProperties lo movio hacia la derecha.).

override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number) : void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);

trace("updateDisplayList");

/*
En este punto pintamos nuestro bloque con el el ancho y alto de 200.
es importante dear de pintar a "bloque si no han ocurrido modificaciones de sus propiedads,para eso usamos la bandera "procesarBloque" que tambien la utilizamos en el commmitproperties
*/
if(procesarBloque)
{
//bloque.graphics.clear();
bloque.graphics.lineStyle(1,0x000000,1);
bloque.graphics.beginFill(Math.random() * 20000000, 1);
bloque.graphics.moveTo(unscaledWidth,unscaledHeight);
bloque.graphics.lineTo(unscaledWidth,0);
bloque.graphics.lineTo(0,0);
bloque.graphics.lineTo(0,unscaledHeight);
}

}

El metodo measure le da el tamaño inicial al componente.

override protected function measure() : void
{
super.measure();
trace("measure");

measuredWidth += 30;
measuredHeight += 30;

}

Luego de crear todos estos metodos debemos ejecutar la aplicacion en modo debugger (para que veamos lo que va arrojando la funcion trace).

Espero que les haya servido este tutorial, cualquier duda que tengan pueden escribirme al mail aalejo@gmail.com o dejar un comentario en la parte de abajo de esta página.

Ejemplo




Continuar leyendo...

Syntax Highlighter - Publicando ejemplos de codigos


Para publicar ejemplos de códigos en una página web (o en un blog como este), es necesario realizar una serie de pasos para evitar que el navegador web los interprete (en caso de ser html) o que elimine pequeños estractos creyendo que debe interpretarlos.

Por otro lado, se pierde mucho la legibilidad del código como consecuencia de una pérdida de la identación (los espacios, tabs, etc.). Como yo siempre he sido partidario de hacer mis ejemplos lo mas entendibles que pueda, no estaba contento con la solución que habia resuelto para publicar ejemplos de códigos hasta la fecha, y estaba buscando como loco una solución que me coloreara los codigos (tal cual como lo hace DreamWeaver, Visual Studio, Eclipse, etc.), que me mantiviera la identacion y que me enumerara las lineas.

Despues de buscar por casi 6 meses ¡¡¡Lo consegui!!! todo el problema era colocar las palabras correctas en google (como siempre), en este caso habia que colocar "Syntax Highlighter" y daríamos con la solución.

Para que vean la diferencia les voy a colocar un ejemplo de como estaba publicando los ejemplos hasta hoy y de como los voy a publicar de aqui en adelante:

Códigos hasta el dia de hoy :S
<?php
function sumar($a,$b)
{
return $a+$b;
}

function restar($a,$b)
{
return $a-$b;
}

echo "La suma es: " . sumar(2,3) . "</br>";
echo "La resta es: " . restar(2,3);

?>
Códigos a partir de hoy :)


<?php
function sumar($a,$b)
{
return $a+$b;
}

function restar($a,$b)
{
return $a-$b;
}

echo "La suma es: " . sumar(2,3) . "</br>";
echo "La resta es: " . restar(2,3);

?>


Como pueden ver hay una gran diferencia ademas de que ahora existe el boton para copiar el codigo de una sola vez sin tener que estar seleccionandolo. Espero que les guste porque todo es para que sea mas entendible. Ahora voy a explicar como instalar este plugin de blogger para los que quieran publicar codigos ejemplos como yo.

Instalacion de Syntax Highlighter para Blogger (Blogspot)

Son unos pasos muy basicos:
  • Ir a esta pagina y hacer click en el botón "Add to Blogger".
  • Serán redireccionados a bloger y deberán introducir el nombre del plugin, yo lo deje vacio para que no apareciera visualmente en el blog, únicamente me interesaba que el codigo javascript se ejecutara dentro de la pagina (y para eso hay que agregarlo al blog).
  • De ahora en adelante pueden publicar códigos siempre que los coloquen dentro de los siguientes tags:



<pre name="code" class="php">
... tu codigo transformado
a caracteres que no sean interpretados
por tu navegador va aqui...
</pre>



  • Es importante cambiar el atributo "class" por el nombre del lenguaje que estemos utilizando, en la siguiente tabla se pueden conocer todos los alias de los lenguajes soportados:
  • Por último es bueno saber que podemos configurar algunas opciones mas como si queremos un botón para expandir y contraer el bloque de codigo, el numero de line por el que deseamos empezar, etc. Para eso entramos a la pagina donde explican la configuracion.
Bueno eso es todo amigos, les dejo un ejemplo de uso de la librería fuera de blogger, es decir, en mi máquina, para hacer esto no tienen mas que importar los javascript y css que vienen dentro de los directorios Scripts y Styles del archivo
SyntaxHighlighter_1.5.1.rar.
Espero que les haya sido de utilidad y cualquier duda no duden en escribirme a aalejo@gmail.com.


Continuar leyendo...

domingo, 1 de febrero de 2009

Video tutorial WebService basico con php


En la version 5.0 de PHP (Desconosco si en las anteriores tambien, pero no lo creo) existe una libreria llamada php_soap.dll, esta librería permite, realizar llamadas a metodos y objetos remotos utilizando el protocolo SOAP.

En este ejemplo veremos de una manera muy sencilla como invocar nuestros dos primeros metodos remotos Suma (Que dado dos numero devuelve la suma de ellos) y Resta (que dados dos numeros devuelve la resta de ellos).

Parte I

Parte II

Para lograr esto debemos cumplir con los siguientes pasos:
  1. Asegurarnos de tener activada la libreria php_soap.dll.
  2. Definir el WSDL del Webservice que vamos a consumir.
  3. Implementar el lado del servidor, donde codificaremos y registraremos las dos funciones.
  4. Deinir el lado del cliente donde invocaremos las dos funciones.
Activando la libreria php_soap.dll

Para hacer esto nos vamos al archivo de configuracion de php llamado php.ini, en este archivo vamos a buscar el php_soap.dll y verificamos que no este comentado, es decir, que no tenga un ";" punto y coma por delante. Algo asi:

;extension=php_shmop.dll
;extension=php_snmp.dll
extension=php_soap.dll
;extension=php_sockets.dll
extension=php_sqlite.dll
Definir el WSDL

Para aprender mejor como definir tu propio WSDL te recomiendo el siguiente artículo de Desarrolloweb.com. Les recomiendo tener este archivo alojado en el servidor donde se encontrará la funcion. En nuestro caso el WSDL quedará de la siguiente manera:

Código scramble.wsdl

<?xml version ='1.0' encoding ='UTF-8' ?>
<definitions name='Scramble'
targetNamespace='http://localhost/tutoriales/webservice/servidor/scramble.wdsl'
xmlns:tns='http://localhost/tutoriales/webservice/servidor/scramble.wdsl'
xmlns:soap='http://schemas.xmlsoap.org/wsdl/soap/'
xmlns='http://schemas.xmlsoap.org/wsdl/'>

<message name='sumarRequest'>
<part name='symbol' type='xsd:string'/>
<part name='symbol2' type='xsd:string'/>
</message>
<message name='sumarResponse'>
<part name='Result' type='xsd:string'/>
</message>
<message name='restarRequest'>
<part name='symbol' type='xsd:string'/>
<part name='symbol2' type='xsd:string'/>
</message>
<message name='restarResponse'>
<part name='Result' type='xsd:string'/>
</message>

<portType name='ScramblePortType'>
<operation name='sumar'>
<input message='tns:sumarRequest'/>
<output message='tns:sumarResponse'/>
</operation>
<operation name='restar'>
<input message='tns:restarRequest'/>
<output message='tns:restarResponse'/>
</operation>
</portType>

<binding name='ScrambleBinding' type='tns:ScramblePortType'>
<soap:binding style='rpc'
transport='http://schemas.xmlsoap.org/soap/http'/>
<operation name='sumar'>
<soap:operation soapAction='urn:localhost-scramble#sumar'/>
<input>
<soap:body use='encoded' namespace='urn:localhost-scramble'
encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'/>
</input>
<output>
<soap:body use='encoded' namespace='urn:localhost-scramble'
encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'/>
</output>
</operation>
<operation name='restar'>
<soap:operation soapAction='urn:localhost-scramble#restar'/>
<input>
<soap:body use='encoded' namespace='urn:localhost-scramble'
encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'/>
</input>
<output>
<soap:body use='encoded' namespace='urn:localhost-scramble'
encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'/>
</output>
</operation>
</binding>

<service name='ScrambleService'>
<port name='ScramblePort' binding='ScrambleBinding'>
<soap:address location='http://localhost/tutoriales/webservice/servidor/sumar.php'/>
</port>
</service>
</definitions>

El archivo scramble.wsdl debe tener el targetNamespace con la ubicacion de si mismo. Tambien debemos recordar colocar la direccion del archivo del servidor (que en este tutorial se llama sumar.php), esta direcicon se coloca en la seccion Service:
<service name="'ScrambleService'">
<port name="'ScramblePort'" binding="'ScrambleBinding'">
<soap:address location="'http://localhost/tutoriales/webservice/servidor/sumar.php'/">
</soap:address>
</port></service>

Implementando el lado del Servidor:


En este lado vamos a crear nuestras dos funciones Sumar y Restar:
function sumar($a,$b)
{
return $a+$b;
}

function restar($a,$b)
{
return $a-$b;
}
Tambien es necesario decirle al cache del SOAP cuales son las funciones de las que le hablamos en el WSDL, para eso colocaremos el siguiente codigo:

// turn off the wsdl cache
ini_set("soap.wsdl_cache_enabled", "0");

$server = new SoapServer("scramble.wsdl");

$server->addFunction("sumar");
$server->addFunction("restar");

$server->handle();

Al final tendremos un archivo llamado "sumar.php" con el siguiente codigo:

Código sumar.php

<?php

function sumar($a,$b)
{
return $a+$b;
}

function restar($a,$b)
{
return $a-$b;
}

// turn off the wsdl cache
ini_set("soap.wsdl_cache_enabled", "0");

$server = new SoapServer("scramble.wsdl");

$server->addFunction("sumar");
$server->addFunction("restar");

$server->handle();

?>


Definir el lado del Cliente

Para el cliente primero creamos un formulario donde se introduciran los dos numeros que deseamos sumar, luego (si ya se recibieron los dos numeros por el metodo POST) lo que tenemos que hacer es crear un objeto SoapClient diciendole la ubicacion de nuestro WSDL, luego podremos llamar a los metodos sumar y restar como si fueran metodos de este objeto. El codigo del archivo client.php nos queda de la siguiente manera:

Código client.php
<form id="form1" name="form1" method="post" action="">
<table width="23%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td>a: </td>
<td><input name="a" type="text" id="a" size="5" /></td>
</tr>

<tr>
<td>b: </td>
<td><input name="b" type="text" id="b" size="5" /></td>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type="submit" name="button" id="button" value="Submit" /></td>
</tr>
</table>
</form>
<?php
if(isset($_POST['a']) && isset($_POST['b']))
{
// turn off the WSDL cache
ini_set("soap.wsdl_cache_enabled", "0");

$client = new SoapClient("http://ejemplos.net78.net/webservice/servidor/scramble.wsdl");

$a = $_POST['a'];
$b = $_POST['b'];

$resultado = $client->sumar($a,$b);
print("La suma de los numeros : $a + $b es: $resultado <br>");

$resultado = $client->restar($a,$b);
print("La resta de los numeros : $a + $b es: $resultado");
}

?>

Lamentablemente no puedo demostrar este ejemplo funcionando, ya que el hosting php con el que trabajo no tiene habilitada la libreria
php_soap.dll. Por ende, solo les puedo dejar un link para descargar el ejemplo.


Continuar leyendo...