Ver Mensaje Individual
  #1  
Viejo 23/09/17, 23:06:54
Array

[xs_avatar]
Rsc Rsc no está en línea
Usuario muy activo
 
Fecha de registro: jun 2011
Mensajes: 502
Modelo de smartphone: Xiaomi Mi5s
Tu operador: Otra
Crear un WebService con PHP - Iteractuar con una DB SQL en la Web

A raíz de una consulta que hice en este foro http://www.htcmania.com/showthread.php?t=1349091 para mejorar la comunicación con una Base de datos (DB de ahora en adelante) ubicada en Dropbox, usando la app Dropsync, al compañero @WillyWeb se lo ocurrió la ingeniosa idea de montar la DB en un alojamiento web gratuito para acceder a ella desde Tasker.

Como ha sido un hilo en el que se ha trabajado a base de ensayo y error, puede ser un poco engorroso de entender, y dado que el que cometía los errores solía ser yo, me veo obligado moralmente a intentar recoger toda la información para que se pueda entender de forma más sencilla.

Antes de continuar, quiero dejar claro que TODO lo que voy a explicar en este tutorial, me lo ha facilitado @WillyWeb, incluso mucho contenido del texto, lo he copiado y pegado literalmente de sus comentarios.

1. PREPARACIÓN DB y SUBDOMINIO

Registrarse en una página que ofrezca alojamiento web. Yo particularmente lo he hecho en la página http://www.atspace.com.

Crear una DB MySQL. Una vez creada se puede administrar desde phpMyAdmin. En mi caso he creado una DB con una tabla denominada goleadores, con la siguiente estructura:

goles (INTYINT) jugador (VARCHAR) usuario (VARCHAR)

Crear un subdominio.
Panel de control > Herramientas de alojamiento > Gestor de dominios > Crear un Subdominio Gratuito.
Selecciona uno de los dominios base que te ofrecen y escribe en la casilla una palabra que te guste. La idea es componer una dirección web de esta forma...

http://miweb.atspace.cc

Esa será la dirección base (el servidor) de la URL del WebService.

2. CREAR UN WEBSERVICE CON PHP

La información se ha obtenido en esta página: http://tosblama.blogspot.com.es/2015...e-con-php.html y las modificaciones necesarias para conseguir hacerlo funcionar, las ha aportado @WillyWeb.

Para entender como funciona, recomiendo entrar en la página, ya que está mucho mejor explicado de lo que lo pueda hacer yo.

Básicamente el sistema se basa en dos archivos. Un PHP (webservice.php) con el código del WebService y un XML (opciones.xml) con las consultas SQL que usa el proyecto.

A continuación copio el código PHP que yo he utilizado, pero antes, es importante recalcar que en necesario cambiar la línea 56 e indicar los datos correspondientes a vuestra DB. Como al copiar el código no se han mantenido los números de las líneas, puede resultar más complicado localizarla, así que busca la palabra "CONTRASEÑA" en el código y tómalo como referencia.

[PHP]<?php
// Recoger parámetros de entrada. Se permite GET y POST
$paramOpcion = $_REQUEST['opcion'];
$paramLista = $_REQUEST['listaParametros'];
$formato = strtolower($_REQUEST['format']) == 'xml' ? 'xml' : 'json';

// Obtener consulta SQL a ejecutar
$sql = getSQL($paramOpcion);
$sql = aplicarParametrosSQL($sql,$paramLista);

// Lanzar consulta a la base de datos
if ($sql == '')
die ('Opcion incorrecta');
else
{
$datos = getArrayDatos($sql);
echo formatData($datos,$formato);
}


// Dado un nombre de opción, buscar en el xml su SQL correspondiente
function getSQL($nombreOpcion)
{
$xmlOpciones = simplexml_load_file('opciones.xml');
$sql = "";
foreach ($xmlOpciones->opcion as $opcion)
{
$nombre = $opcion->nombre;
if ($nombre == $nombreOpcion)
{
$sql = $opcion->SQL;
break;
}
}
return $sql;
}

// Aplicar parámetros de la sentencia SQL
function aplicarParametrosSQL($sql, $listaPametros)
{
if ($listaPametros != '')
{
$arrParam = explode(':',$listaPametros);
for ($i=0; $i<count($arrParam); $i++)
{
$nomParam = '#PARAMETRO_'.($i+1).'#';
$valor = $arrParam[$i];
$sql = str_replace($nomParam, $valor, $sql);
}
}
return $sql;
}

function getArrayDatos($sql)
{
$db = mysqli_connect('fdb17.atspace.me','codigo_promunio ','CONTRASEÑA','codigo_promunio') or die('No se pudo conectar con la base de datos');
//mysql_select_db('2454802_promunio',$db) or die('No se pudo seleccionar la base de datos.');
$resul = mysqli_query($db,$sql) or die('Consulta incorrecta: '.$sql);

/* create one master array of the records *
$datos = array();
if(mysqli_num_rows($resul))
{
while($row = mysqli_fetch_assoc($resul))
{
$datos[] = $row;
}
}

/* disconnect from the db *
mysqli_close($db);

/* Return data array *
return $datos;
}

function formatData($data,$format='json')
{
if($format == 'json' || $format == 'jsonp')
{
header('Content-type: application/json');
if (isset($_REQUEST['callback'])) // Peticiones ajax
return $_REQUEST['callback'].'('.json_encode($data).')';
else
return json_encode($data);
}
else
{
header('Content-type: text/xml');
$xml = new SimpleXMLElement('<response/>');
return array2xml($data, $xml);
}
}

function array2xml($array, $xml = false)
{
if($xml === false)
{
$xml = new SimpleXMLElement('<root/>');
}
foreach($array as $key => $value)
{
if(is_array($value))
{
//array2xml($value, $xml->addChild($key));
array2xml($value, $xml->addChild('item'));
}
else
{
$xml->addChild($key, htmlspecialchars($value));
}
}
return $xml->asXML();
}

?>[/PHP]

Y a continuación pongo el código XML.

Código:
<?xml version="1.0" encoding="utf-8" ?>
<OpcionesDisponibles>
  <opcion>
    <nombre>listajugadores</nombre>
    <SQL>SELECT goles,jugador,usuario FROM goleadores</SQL>
  </opcion>
    <opcion>
    <nombre>insertar</nombre>
    <SQL>UPDATE goleadores SET goles = (goles+1) WHERE jugador LIKE "MESSI"</SQL>
  </opcion>
</OpcionesDisponibles>
Si os fijáis bien en el XML tienes varios bloques <opcion>...</opcion>, uno por cada sentencia SQL que se desee lanzar. Dentro de cada <opcion> tienes un <nombre>...</nombre> y un <SQL>...</SQL>.

En <nombre> se puede poner lo que queráis pero recomiendo usar términos descriptivos del propósito de la sentencia SQL. Si has creado una sentencia para listar los goles de un jugador yo usaría 'golesjugador'. Si tienes otra para obtener el listado de todos los jugadores podrías usar 'listajugadores'. Y así con todas las demás sentencias necesarias.

La palabra que pongamos entre las etiquetas <nombre>, será la que debamos usar en la acción HTTP Get, para ejecutar la sentencia SQL deseada. Eso se detallará más adelante.

Evidentemente en el campo <SQL> hay que poner la sentencia SQL, pero con un pequeño matiz. Si esa sentencia tiene un parámetro que pueda ser variable (como el nombre del jugador) debes poner '#PARAMETRO_1#' en su lugar (mira la línea 9 del XML del siguiente enlace (https://pastebin.com/jbWf708h). Si la sentencia tiene más de un parámetro de ese tipo pones '#PARAMETRO_1#' y '#PARAMETRO_2#'. Y así con todos los parámetros que necesite la sentencia.

3. DE VUELTA AL PANEL DE CONTROL DEL ALOJAMIENTO (HOSTING)

Una vez creados los archivos webservice.php y opciones.xml como se ha explicado en el punto anterior, hay que copiarlos a la raíz de la DB, utilizando un gestor de archivos que se muestra al hacer click sobre la etiqueta "Administrar archivos".

4. TASKER


Ejecutamos una acción HTTP Get, e indicamos en la pestaña Servidor:Puerto lo siguiente:

Código:
http:/Nombre del subdominio/webservice.php?opcion=nombreopción&format=json.
En mi caso concreto pongo esto:

Código:
http:/pruminio.atspace.eu/webservice.php?opcion=listajugadores&format=json
La parte de la URL con la que se selecciona el formato (&format=json) es opcional. Si se indica, el valor por defecto es "json"

Seguramente os hayáis dado cuenta que el nombre de opción listajugadores es el que usado en el código xml.

<nombre>listajugadores</nombre>

Como ya he comentado, se usa para determinar que sentencia SQL vamos a ejecutar.

Lo que devuelve el WebService es un objeto JSON y en mi caso queda así, y queda alojado en la variable incorporada %HTTPD

[{"goles":"7","jugador":"RONALDO","usuario":"SANDRO "},{"goles":"8","jugador":"MORATA","usuario":"BIFU "},
{"goles":"9","jugador":"LUIS SUAREZ","usuario":"RICHY"},{"goles":"15","jugador" :"MESSI","usuario":"RAUL"}]


También podemos abrir el enlace desde el navegador, y deberemos de obtener el mismo resultado.

A día de hoy, todavía no he logrado obtener únicamente el contenido de los campos de la tabla, pero según indica WillyWeb, es facil de procesar con Javascript. En cuanto tenga más información la añadiré.
SOLUCIÓN: http://www.htcmania.com/showthread.php?t=1349091&page=4

La acción "Consulta SQL" devuelve el resultado en un array. Cada elemento del array es una fila del resultado. Cada elemento/fila contiene los valores de las columnas separados por el símbolo que pongamos en el campo correspondiente de la acción.

Lo que el WebService devuelve es un JSON compuesto por un array de objetos. Cada elemento del array es una fila del resultado. Cada elemento/fila contiene los valores de las columnas en un objeto con parejas "nombre_columna : valor" separadas por comas.

En un vistazo rápido vemos que "casi" es lo mismo, pero sobra lo de los objetos y los nombres de las columnas. Pues quitamos lo que sobra y listo...

Código:
ConvierteJSONenArrayLocal (999)
	A1: HTTP Get [ Servidor:pruminio.atspace.eu Ruta:webservice.php Atributos:opcion=listajugadores ] 
	A2: JavaScriptlet [ Código://Convierte JSON a un array local
json=global('HTTPD');

json=json.replace(/"goles":/g,"")
json=json.replace(/"jugador":/g,"")
json=json.replace(/"usuario":/g,"")
json=json.replace(/"listado":/g,"")

json=json.replace(/"/g,"") //dos comillas dobles

json=json.replace(/{|}/g,'"') //comilla simple + doble + simple

var array=[]
array=JSON.parse(json)
	A3: Flash [ Texto:%array(#) registros ]
Esta tarea lanza la consulta al WebService (A1). Procesa el JSON usando JS convirtiéndolo en un array similar al de la acción "Consulta SQL" (A2). Y finalmente muestra el tonal de filas/elementos obtenidos (A3).

Para guardar el resultado en un array global en vez de en un array Global, hay que utilizar este otro código, que funciona perfectamente a pesar de que la documentación de Tasker indica que no se pueden crear arrays globales desde JS

Código:
ConvierteJSONenArrayGlobal (666)
	A1: HTTP Get [ Servidor:pruminio.atspace.eu Ruta:webservice.php Atributos:opcion=listajugadores ] 
	A2: Array Clear [ Matriz de Variables (array):%MiArray ] 
	A3: JavaScriptlet [ Código://Convierte JSON a un array global
json=global('HTTPD');

json=json.replace(/"goles":/g,"")
json=json.replace(/"jugador":/g,"")
json=json.replace(/"usuario":/g,"")
json=json.replace(/"listado":/g,"")

json=json.replace(/"/g,"") //dos comillas dobles

json=json.replace(/{|}/g,'"') //comilla simple + doble + simple

json=JSON.parse(json)

for(ind=0;ind<json.length;ind++)
{
nom="MiArray"+(ind+1)
setGlobal(nom,json[ind])
}
	A4: Flash [ Texto:%MiArray(#) registros ]
En la versión para array local el nombre del array se define en estas dos líneas del JavaScriptlet...

var array=[]
array=JSON.parse(json)


En la versión para array global el nombre se define en esta otra línea...

nom="MiArray"+(ind+1)

*Hay que tener en cuenta que ese nombre es el mismo que hay que poner en la acción "Array Clear" que va justo antes de la acción JavaScriptlet.

Funcionamiento de este JavaScriptlet paso a paso...

Código:
//Convierte JSON a un array global
json=global('HTTPD')

json=json.replace(/"goles":/g,"")
json=json.replace(/"jugador":/g,"")
json=json.replace(/"usuario":/g,"")
json=json.replace(/"listado":/g,"")

json=json.replace(/"/g,"") //dos comillas dobles

json=json.replace(/{|}/g,'"') //comilla simple + doble + simple

json=JSON.parse(json)

for(ind=0;ind<json.length;ind++)
{
nom="MiArray"+(ind+1)
setGlobal(nom,json[ind])
}
El JSON que devuelve el WebService tiene esta pinta...

Código:
[{"goles":"7","jugador":"RONALDO","usuario":"SANDRO "},{"goles":"8","jugador":"MORATA","usuario":"BIFU "},
{"goles":"9","jugador":"LUIS SUAREZ","usuario":"RICHY"},{"goles":"15","jugador" :"MESSI","usuario":"RAUL"}]
Y ya hemos quedado en que tenemos que convertirlo en algo similar a lo que devuelve la acción "Consulta SQL", es decir, una array de elementos (filas) con valores (columnas) separados por comas (o lo que queramos).

Esta línea...
json=global('HTTPD')
...pasa la variable global %HTTPD a JS usando la función global(), que es un añadido de Tasker. El valor queda almacenado en la variable "json".

Este bloque...
json=json.replace(/"goles":/g,"")
json=json.replace(/"jugador":/g,"")
json=json.replace(/"usuario":/g,"")
json=json.replace(/"listado":/g,"")
...quita los nombres de los campos. Puedes añadir tantas líneas como necesites.

Esta línea...
json=json.replace(/"/g,"") //dos comillas dobles
...quita todas las comillas dobles.

Esta línea...
json=json.replace(/{|}/g,'"') //comilla simple + doble + simple
...cambia las llaves (abrir y cerrar) por una comilla doble.

Después de pasar por esas líneas el JSON inicial debería tener esta pinta...

Código:
["7,RONALDO,SANDRO","8,MORATA,BIFU","9,LUIS SUAREZ,RICHY","15,MESSI,RAUL"]
Ahora tenemos que decidir si queremos el resultado en un array local o global.

Si queremos el resultado en un array global pondremos esta línea...
json=JSON.parse(json)
...con la que convertimos ese JSON "modificado" en un array haciendo uso del "parser" JSON de JavaScript.

Y luego pondremos este bucle...
for(ind=0;ind<json.length;ind++)
{
nom="MiArray"+(ind+1)
setGlobal(nom,json[ind])
}
...que hace lo que se supone que no se puede hacer, pasar un array global de JS a Tasker. El bucle recorre cada elemento del array "json", crea un nombre para cada elemento del array global de Tasker con su número de índice corregido (uno más que en JS) y pasa el valor del array a esa variable de Tasker usando su función añadida setGlobal().
Si quisieramos el resultado en un array local deberíamos poner esto otro...

var array=[]
array=JSON.parse(json)

El matíz está en esa línea "var array=[]" que define la variable "array" como un array local para la tarea Tasker que contiene el JavaScriptlet.

Última edición por Rsc Día 29/10/17 a las 13:21:19.
Responder Con Cita
Los siguientes 4 usuarios han agradecido a Rsc su comentario:
[ Mostrar/Ocultar listado de agradecimientos ]