Mostrando entradas con la etiqueta XML. Mostrar todas las entradas
Mostrando entradas con la etiqueta XML. Mostrar todas las entradas

martes, 19 de enero de 2010

PHP y PostgreSQL: servicio de consultas XML

El presente artículo se centra en la creación de un servicio que retorna un XML con el resultado de consultas a bases de datos PostgreSQL. Dicho servicio puede ser invocado desde cualquier programa, como si se tratase de un servicio Web (en nuestro caso será un servicio HTTP).

El planteamiento para este servicio, es poder suministrar una serie de parámetros vía URL, para ir conformando la consulta:

- fields (obligatorio). Lista de campos de la consulta
- tables (obligatorio). Lista de tablas de la consulta
- criteria (opcional). Condiciones que ha de cumplir la consulta
- order (opcional). Campos por los que ordenar la consulta
- group (opcional). Campos por los que agrupar la consulta.
- having (opcional). En caso de haber especificado una agrupación, indica las condiciones a cumplir por dicho agrupamiento.

Internamente, el servicio creará la consulta SELECT correspondiente a partir de estos parámetros, procesando la consulta y retornando el XML correspondiente.

El formato del XML retornado, en caso de éxito sería el siguiente:

<query_result>
  <tuple>
    <nombre-campo>valor-campo</nombre-campo>
    ...
  </tuple>
  ...
</query_result>

El formato del XML retornado, en caso de error, sería el siguiente:

<query_result>
  <result>NOK</result>
  <message>mensaje-error</message>
</query_result>

El código para dicho servicio sería el siguiente:

<?php
 $pfields=$_GET["fields"];
 $ptables=$_GET["tables"];
 $pcriteria=$_GET["criteria"];
 $porder=$_GET["order"];
 $pgroup=$_GET["group"];
 $phaving=$_GET["having"];
 
 $dbhost='localhost';
 $dbport='5432';
 $dbname='trazalogic';
 $dbusr='trazalogic';
 $dbpasw='trzlgc';
 $dbcon="host=".$dbhost." port=".$dbport.
  " dbname=".$dbname." user=".$dbusr." password=".$dbpasw;
 
 $xmlresult="<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n\n";
 $xmlresult.="<query_result>\n";
 
 $con = pg_pconnect($dbcon);
 
 if (!$con) {
  $xmlresult.="<result>NOK</result>\n";
  $xmlresult.="<message>";
  $xmlresult.='Se produjo un error al intentar conectar con la base de datos';
  $xmlresult.="</message>\n";
 }
 else {
  $qry="SELECT ".$pfields." FROM ".$ptables;
  
  if (strlen($pcriteria)>0)
   $qry.=" WHERE ".$pcriteria;
  
  if (strlen($porder)>0)
   $qry.=" ORDER BY ".$porder;
  
  if (strlen($pgroup)>0) {
   $qry.=" GROUP BY ".$pgroup;
   if (strlen($phaving)>0)
   $qry.=" HAVING ".$phaving;
  }

  $result = pg_query($con, $qry);
  
  if (!$result) {
   $xmlresult.="<result>NOK</result>\n";
   $xmlresult.="<message>";
   $xmlresult.='Error al ejecutar la consulta ['.$qry.
   "] ".pg_last_error($con);
   $xmlresult.="</message>\n";  
  }
  else {
   $numfields=pg_num_fields($result);
  
   while ($tuple=pg_fetch_row($result)) {
    $xmlresult.=" <tuple>\n";
    for ($i=0;$i<$numfields;$i++) {
    $fld=pg_field_name($result, $i);
    $xmlresult.="<".$fld.">".$tuple[$i]."</".$fld.">";
     $xmlresult.="";
    }
   
    $xmlresult.=" </tuple>\n";
   }
  }
  pg_close($con);
 }
 
 $xmlresult.="</query_result>\n";
 echo $xmlresult;

?>

El servicio en acción sería como se muestra en la siguiente imagen (prestar atención a los parámetros pasados en la URL):


La principal aplicación de este sencillo servicio es poder invocar desde interfaces de usuario (como las RIA's) y pintar los resultados. Esto evitaría tener que definir un servicio por cada consulta, con el consiguiente aumento de productividad. Por contra, cualquier cambio o corrección en una consulta afectaría directamente a la interfaz (no a la capa de negocio).

Safe Creative #1001195346088

domingo, 4 de enero de 2009

Problemas al acceder a ficheros XML en Flex

Trasteando un poco con Flex con una sencilla aplicación que publicaré con gusto en este nuestro blog, he tenido tres problemas que voy a comentar, y que seguro serán de gran utilidad, pues seguramente más de uno se tope con ellos.

El primero de los problemas que me topé es a la hora de intentar realizar una aplicación que lea de un fichero XML. Para ello, lo primero que se nos viene a la cabeza es utilizar el objeto XML, de esta manera:

<mx:XML id="idObjetoXML" source="ruta/archivo.xml"/>

Si bien funciona en tiempo de desarrollo, el problema estará a la hora utilizarlo en otra ruta distinta a la del entorno de desarrollo, ya que el compilador de Flex incluirá este fichero a nuestro archivo compilado, y las rutas dejarán de funcionar.

Para evitar esto, hemos de usar el objeto HTTPService, usando la ruta del fichero XML como URL, y después asignar el resultado al objeto XML. He aquí el ejemplo.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete="getImages.send()"
>

<mx:Script>
<![CDATA[
import mx.rpc.Fault;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.controls.Alert;

[Bindable]
private var objetoXML:XML;

private function
resultHandler(event:ResultEvent):void
{
objetoXML=event.result as XML;
}

private function
faultHandler(event:FaultEvent):void
{
Alert.show(event.fault.message,
"Error al cargar fichero XML");
}

]]>
</mx:Script>

<mx:HTTPService
id="getImages"
contentType="application/xml"
resultFormat="e4x"
url="ruta/archivo.xml"
method="GET"
useProxy="false"
result="resultHandler(event)"
fault="faultHandler(event)"
/>

...


El objeto "HTTPService" será invocado al crearse la aplicación (evento "creationComplete" del objeto "Application"), y será el encargado de leer el fichero XML. Si el fichero se lee con éxito, se invocará al método "resultHandler", pasando en el parámetro "event" el resultado. En este método, se asignará al objeto de tipo "XML" este resultado en dicho formato. Si hay algún error, se invocará el método "faultHandler".

El segundo problema con el que podemos toparnos es cuando intentamos ejecutar la aplicación en otro entorno distinto al de desarrollo, sobre todo, si se pretende ejecutar en local. La ejecución daría un error del tipo:

faultCode:InvokeFailed faultString:’Error #2148:
SWF file 'ruta/fichero.swf' cannot access local
resource fichero.xml, Only local-with-filesystem
and trusted local SWF files may access local
resources.’


Debido a la seguridad que ofrece Flex, no se puede acceder a ficheros locales. Para saltar esta seguridad, habrá que jugar con los parámetros de compilación.

Para ello, seguir estas instrucciones:
1) En el panel de navegación de Flex Builder, hacer clic derecho sobre el nodo del proyecto.
2) Seleccionar "Properties", con lo que se abrirá el cuadro de diálogo de propiedades del proyecto.
3) En el árbol de la izquierda, seleccionar la opción "Flex compiler"
4) En el campo "Additional compiler", añadir el parámetro "-use-network=false"

El tercero de los problemas llegó a la hora de subirlo al servidor web. A la hora de acceder al fichero xml me daba el siguiente error:

faultCode:Server.Error.Request faultString:
'HTTP request error' faultDetail:'Error:
[IOErrorEvent type="ioError" bubbles=false
cancelable=false eventPhase=2 text="Error #2032:
Error de secuencia. URL: URLruta/archivo.xml"].
URL: URLruta/archivo.xml'


Este error se produce en el objeto HTTPService:

<mx:HTTPService
id="getImages"
contentType="application/xml"
resultFormat="e4x"
url="URLruta/archivo.xml"
showBusyCursor="true"
useProxy="false"
result="resultHandler(event)"
fault="faultHandler(event)"
/>


Aunque en teoría está bien definido, en realidad, el problema lo está dando el atributo "contentType". Ello se resuelve cambiando este atributo por:

contentType="application/x-www-form-urlencoded"


De esta manera, ya funcionará sin problemas.

Safe Creative #1001195348662