- EL CONOCIMIENTO ES Y DEBE SER LIBRE -

sábado, junio 19, 2010

Cómo integrar SmartGWT con PHP usando DataSource?

SmartGwt no es otro tradicional framework Ajax que solo presenta y muestra datos via XML/Json u otro mecanismo (de estos hay bastantes!!!).  SmartGwt fue concebido para la administración del estado del lado del cliente y la propagación de cambios en el servidor. Por ejemplo, cuando tenemos varias grillas mostrando datos de una tabla y hemos añadido un registro por medio de un formulario, en ese instante deben de actualizarse las grillas automáticamente. Lo anterior se consigue con un mecanismo denominado databinding, lo cual significa que todas las grillas(widgets) se han enlazado al DataSource. Todas las operaciones CRUD se reflejaran automáticamente a todos los widgets enlazados.
A continuación implementaremos un ejemplo bastante básico de como SmartGwt se integra con PHP:

/*
* Codigo fuente de DataSourceCountry.java
*/
public class DataSourceCountry extends RestDataSource{

 private static DataSourceCountry instance = null;    
 
 public static DataSourceCountry getInstance() {  
       
     if (instance == null) {  
            
            instance = new DataSourceCountry();
            
        }  
        return instance;  
    }//fin de funcion
 static void AddData(Record record)
    {
     instance.addData(record);   
    }//fin de funcion 
    static void RemoveData(Record record)
    {
     instance.removeData(record);     
    }//fin de funcion
//public DataSourceCountry() Corregido por el amigo anonimo
    private DataSourceCountry()
    {
      DataSourceIntegerField id = new DataSourceIntegerField("id", "CATEGORIA ID"); 
   id.setPrimaryKey(true);  
   
   DataSourceTextField idcategoria = new DataSourceTextField("idcategoria", "PERTENECE A");  
   idcategoria.setForeignKey("id");  
   idcategoria.setRootValue("");  
 
   DataSourceTextField descripcion = new DataSourceTextField("descripcion", "DESCRIPCION");   
   DataSourceTextField categoria = new DataSourceTextField("categoria", "CATEGORIA");

   this.setFields(id,idcategoria,descripcion,categoria);  
   
   OperationBinding fetch = new OperationBinding();
   fetch.setOperationType(DSOperationType.FETCH);
   fetch.setDataProtocol(DSProtocol.POSTPARAMS);
   
   OperationBinding add = new OperationBinding();
   add.setOperationType(DSOperationType.ADD);
   add.setDataProtocol(DSProtocol.POSTPARAMS);
           
   OperationBinding remove = new OperationBinding();
   remove.setOperationType(DSOperationType.REMOVE);
   remove.setDataProtocol(DSProtocol.POSTPARAMS);
            
   this.setOperationBindings(fetch,add,remove);
   this.setDataFormat(DSDataFormat.JSON); 
  
   this.setFetchDataURL("fetch.php");  //integracion con PHP
   this.setAddDataURL("add.php");      //integracion con PHP
   this.setRemoveDataURL("remove.php");//integracion con PHP
    }
}
<?php
/*
* Codigo fuente de fetch.php
*/
$data=array();
$data[0]['id']=1;
$data[0]['descripcion']='descripcin plazas';
$data[0]['categoria']='plazas';
$data[0]['idcategoria']='';

$data[1]['id']=2;
$data[1]['descripcion']='descripcin comedores';
$data[1]['categoria']='comedores';
$data[1]['idcategoria']='';

$mensaje=array();
$mensaje['response']['status']=0;
$mensaje['response']['data']=$data;

echo json_encode($mensaje);
?>
En el archivo "remove.php" podemos notar que al remover un registro el DataSource envía el "id" del registro a eliminar, entonces el Servidor debe de responder con el "id" y un "status=0".
<?php
/*
* Codigo fuente de remove.php
*/
$data=array();
$data[0]['id']=$_POST['id'];

$mensaje=array();
$mensaje['response']['status']=0;
$mensaje['response']['data']=$data;

echo json_encode($mensaje);
?>
En el archivo "add.php" el DataSource envía el campo "descripción" y "categoria" enseguida el Servidor le devuelve los campos pero aumentando los campos "id" y "idcategoria".
<?php
/*
* Codigo fuente de add.php
*/
$data=array();
$data[0]['id']=3;
$data[0]['descripcion']=$_POST['descripcion'];
$data[0]['categoria']=$_POST['categoria'];
$data[0]['idcategoria']='';

$mensaje=array();
$mensaje['response']['status']=0;
$mensaje['response']['data']=$data;

echo json_encode($mensaje);
?>
Explicare un poco el archivo "Probandatasource.java". En la linea 21,37,41 realizamos el databinding con el DataSource con los Widgets TreeGrid,ListGrid y SelectItem respectivamente. En la linea 50 creamos un registro y lo enviamos al DataSource (al pulsar el botón añadir). En la linea 65 capturamos el "id" del registro seleccionado y lo enviamos al DataSource(al pulsar el botón eliminar)
/*
* Codigo fuente de Probandodatasource.java
*/
public class Probandodatasource implements EntryPoint {

 public void onModuleLoad() {
  final TreeGrid  countryGrid = new TreeGrid ();
        countryGrid.setCanFreezeFields(true);         
        countryGrid.setWidth("100%");  
        countryGrid.setHeight("100%");  
        countryGrid.setShowAllRecords(true);
        countryGrid.setLoadDataOnDemand(false);
       
        TreeGridField  descripcion = new TreeGridField ("descripcion", "Descripcion", 200);  
        TreeGridField  categoria = new TreeGridField ("categoria", "Categoria",200);  
  
        countryGrid.setFields(categoria, descripcion);  
        countryGrid.setCanResizeFields(true); 
        countryGrid.setAutoFetchData(true);

        countryGrid.setDataSource(DataSourceCountry.getInstance());
         
        final ListGrid  countryGridList = new ListGrid ();
        countryGridList.setCanFreezeFields(true);         
        countryGridList.setWidth("100%");  
        countryGridList.setHeight("100%");  
        countryGridList.setShowAllRecords(true);
        //countryGridList.setLoadDataOnDemand(false);
       
        ListGridField  descripcionList = new ListGridField ("descripcion", "Descripcion", 200);  
        ListGridField  categoriaList = new ListGridField ("categoria", "Categoria",200);  
  
        countryGridList.setFields(categoriaList, descripcionList);  
        countryGridList.setCanResizeFields(true); 
        countryGridList.setAutoFetchData(true);

        countryGridList.setDataSource(DataSourceCountry.getInstance());
        
        final SelectItem combobox = new SelectItem("ComboBox");  
    combobox.setWidth(240);  
    combobox.setOptionDataSource(DataSourceCountry.getInstance());  
    combobox.setDisplayField("categoria");  
    combobox.setPickListWidth(450);  
    combobox.setPickListFields(categoriaList, descripcionList); 
        
 
        IButton btnAnadir=new IButton("Añadir");
        btnAnadir.addClickHandler(new ClickHandler(){

   @Override
   public void onClick(ClickEvent event) {
    ListGridRecord rec = new ListGridRecord();    
    rec.setAttribute("idcategoria","");                          
                                rec.setAttribute("descripcion","descripcion de jardines");  
                                rec.setAttribute("categoria","jardines");  
    
    DataSourceCountry.AddData(rec);
   }
         
        });
        
        IButton btnRemove=new IButton("Remover");
        btnRemove.addClickHandler(new ClickHandler(){

   @Override
   public void onClick(ClickEvent event) {
    ListGridRecord id=countryGrid.getSelectedRecord();
    
    DataSourceCountry.RemoveData(id);
    }
         
        });
        
        VLayout vl=new VLayout();
        vl.setWidth100();
        vl.setHeight100();
        vl.addMember(countryGrid);
        vl.addMember(countryGridList);
        
        DynamicForm df=new DynamicForm();
        df.setItems(combobox);
        vl.addMember(df);
        
        vl.addMember(btnAnadir);
        vl.addMember(btnRemove);
        vl.draw();
        
 }
}
La implementación de este pequeñísimo ejemplo lo pueden ver en el video, cabe mencionar que copio y pego el código fuente de este blog a Eclipse; tal que, cualquiera con algo de entusiasmo y ganas lo pueda ejecutar.





Nota: Tengo la terrible costumbre de enfocarme en el funcionamiento, en lo próximos posts tratare de hacer ejemplos mucho mas estéticos!

18 comentarios:

Alberto Cuéllar dijo...

Hola Jumanor, excelente tutorial de como integrar PHP con SmartGWT. Solo tengo una duda. Como evitas los problemas de cross-site calls al utilizar gwt y php. Esto te lo comento porque yo he estado utilizando XJSONDatasource pero ya he tenido muchos problemas con él y queria mejor utilizar RestDataSource. Me podrías orientar un poco en como resolver esto. Esque ademas estoy utilizando el framework zend de php entonces tengo los archivos php fuera del war de la applicación en gwt.

Anónimo dijo...

Un comentario rápido. Estaba viendo tu código del datasource, y me parece que se te pasó poner el constructor como privado =)

Jorge Cotrado dijo...

Hola, RestDataSource no te soluciona el problema de cross-domain, lo que hago es habilitar un host virtual que me direccione a la ruta del “WAR” luego configuro el Eclipse para que me levante el Host Virtual (http://probandodatasource/probandodatasource.html). Todo los archivo php están dentro de este Host Virtual por lo que no tengo problemas de cross-domain.

Yo he usado Symfony (supongo que es parecido a ZEND) con GWT , del siguiente modo(bastante chapucera jeje):
-Compilaba la aplicación GWT y le ordenaba al compilador que el JavaScript generado lo enviara a la ruta deseada (toma mucho tiempo la compilación)
-Depuraba con Firebug.

Actualmente trabajo con el Patron MVP junto con Doctrine ORM, puedo depurar la vista y no pierdo tiempo en la compilación, por el momento, soy feliz.

El día que Symfony o Zend se integre con GWT …. mmmm

Jorge Cotrado dijo...

Ok, contructor privado.thanks

Alberto Cuéllar dijo...

Como aplicas el patrón MVP junto con el ORM Doctrine?

Saludos.

Jorge Cotrado dijo...

En un proximo post mostrare un ejemplo de MVP, espero explicativo

Alberto Cuéllar dijo...

Hola Jumanor queria pedir tu ayuda para hacer un form.saveData(new DSCallBack). Esque he estado intentado hacerlo pero no me sale. Deseo que la accion save me devuelva el Id creado en la base de datos.

Este es el código que tengo pero me devuelve null.

btn_guardar.addClickHandler(new ClickHandler() {

@Override
public void onClick(ClickEvent event) {
form_altapedido.saveData(new DSCallback() {

@Override
public void execute(DSResponse response, Object rawData, DSRequest request) {
Record rs[] = response.getData();

String id = rs[0].getAttributeAsString("id");

rs[0].getAttributeAsString("id");
System.out.println("after request is completed this method is executed" + id);
}
});

Me podrias ayudar por favor.

Jorge Cotrado dijo...

Hola Alberto,
Mira esto lo hago asi:
Revisa la linea 17 de la clase DataSourceCountry alli modifica de tal modo que quede asi:

static void AddData(Record record) { //instance.addData(record); instance.addData(record, new DSCallback(){
@Override
public void execute(DSResponse response, Object rawData,
DSRequest request) {
// TODO Auto-generated method stub
Record[] rs = response.getData(); String cad=rs[0].getAttributeAsString("id"); SC.say(cad);
}});
}// end function

Jorge Cotrado dijo...
Este comentario ha sido eliminado por el autor.
Jorge Cotrado dijo...

Hola,
El codigo que muestras deberia de funcionar,por lo menos en teoria.
Yo no actualizo la data con DynamicForm sino directamente a traves del RestDataSource.

Saludos.

Alberto Cuéllar dijo...

Hola Jumanor, disculpa, tendras un ejemplo de como hacer un fileupload con gwt. He intentado hacerlo con el widget fileupload pero no me sale. Agradezco tu ayuda.

maestroscuro dijo...

Hola,

really nice tutorial! And the screencasts are great! Good job!

KhaosZen dijo...

hola jumanor. Muy buen tutorial.
muy agradecido.

Yo estoy haciendo un par de cosas con GWT y tambien tengo el problema ese de cross site, lo solucione usando el smartgwtee demo, pero claro expira asi que intentare hacer eso del virtual host.

Una pregunta. Nunca me quedo claro el uso de setForeignKey.
Sobretodo que relacion tiene con 2 datasources porque tambien vi que se lo usa asi:
categoryField.setForeignKey("supplyCategoryDS.categoryName");

Saludos y excelente tutorial.

Jorge Cotrado dijo...

Hola, en el ejemplo se esta relacionado 1 DataSource consigo mismo.

En una grilla maestro-detalle se relacionan 2 Datasource.

En ambos casos usamos setForeignKey para relacionarlos.

Saludos!!!

favrycio dijo...

Hola Jumanor,

De tanto buscar documentacion que sirva de gwt llegue a tu blog, tengo una pregunta a la que no he encontrado respuesta, como huardo imagenes con gwt, tienes algun ejemplo pratico, los datos debo enviarlos a un servicio REST

Anónimo dijo...

Ya me suponia yo que trabajar con imagenes era complicado, para que no respondas debe ser bien dificil.

Aca algo de luz sobre la oscuridad:

http://www.smartclient.com/smartgwtee/showcase/#upload_sql

Salu2

Paket dijo...

Hola , que buen ejemplo, ya realize todo solo que no he podido con lo del hostvirtual para que corra la aplicación con los php, corre pero sin traducir el php, podrías decirme como hacer eso ?

Gracias

Jorge Cotrado dijo...

http://jumanor.blogspot.com/2010/05/ejecutar-y-depurar-gwt-sobre-el.html