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