PHP + MySQL + HighChart: Mostrar varias gráficas dinámicamente
Hola a todos, son varios los que me han pedido mostrar varias gráficas a la vez con la librería HighChart. En el tutorial que hice hace un tiempo sobre PHP + MySQL + HighCharts podemos ver cómo realizar una consulta a una base de datos y mostrar una gráfica con los mismos.
En este tutorial vamos a suponer que tenemos una base de datos con una tabla compuesta de cuatro campos: energy, water, temperature y time. Nuestro proposito será extraer de una consulta todos estos datos y mostrarlos en una gráfica. Vamos a suponer que los campos energy, water, temperature, son de tipo "VARCHAR" (Float tambien nos serviría), Y el campo time es de tipo "TIMESTAMP". Para realizar esta tarea vamos a ejecutar los siguientes pasos:
1º Crear una gráfica estática con valores predeterminados.
2ºExtraer un array multidimensional con todos los datos de la tabla (energy, water, temperature y time)
3ºCambiar el valor del campo time por su equivalencia en milisegundos.
4º Crear una gráfica dinámica.
1º Crear una gráfica estática con valores predeterminados
En un principio vamos a poner un ejemplo de como realizar una gráfica estática con varios campos.
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <title>Java Smart Home Simulator</title> <!-- Latest compiled and minified CSS --> </head> <body> <!-- Latest compiled and minified JavaScript --> <script src="https://code.jquery.com/jquery.js"></script> <!-- Importo el archivo Javascript de Highcharts directamente desde su servidor --> <script src="http://code.highcharts.com/stock/highstock.js"></script> <script src="http://code.highcharts.com/modules/exporting.js"></script> <div id="container"> </div> <script type='text/javascript'> $(function () { $(document).ready(function() { Highcharts.setOptions({ global: { useUTC: false } }); var chart; $('#container').highcharts({ chart: { type: 'spline', animation: Highcharts.svg, // don't animate in old IE marginRight: 10, events: { load: function() { } } }, title: { text: 'Physical Variables' }, xAxis: { type: 'datetime', tickPixelInterval: 150 }, yAxis: { title: { text: 'Value' }, plotLines: [{ value: 0, width: 1, color: '#808080' }] }, tooltip: { formatter: function() { return '<b>'+ this.series.name +'</b><br/>'+ Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', this.x) +'<br/>'+ Highcharts.numberFormat(this.y, 2); } }, legend: { enabled: true }, exporting: { enabled: true }, series: [{ name: 'Energy Consumption', data: (function() { var data = []; data.push([1400976011000,0.000]); data.push([1400976017000,0.000]); data.push([1400976018000,0.000]); data.push([1400976020000,2.625]); data.push([1400976022000,2.785]); data.push([1400976024000,3.685]); data.push([1400979627000,2.785]); data.push([1400979629000,4.160]); data.push([1400979633000,4.160]); data.push([1400981436000,4.160]); data.push([1400981437000,4.160]); data.push([1400981440000,4.160]); return data; })() },{ name: 'Water Consumption', data: (function() { var data = []; data.push([1400976011000,0.000]); data.push([1400976017000,0.000]); data.push([1400976018000,0.000]); data.push([1400976020000,0.000]); data.push([1400976022000,0.000]); data.push([1400976024000,9.500]); data.push([1400979627000,0.000]); data.push([1400979629000,0.000]); data.push([1400979633000,0.000]); data.push([1400981436000,0.000]); data.push([1400981437000,0.000]); data.push([1400981440000,0.000]); return data; })() },{ name: 'Temperature', data: (function() { var data = []; data.push([1400976011000,0.000]); data.push([1400976017000,0.000]); data.push([1400976018000,0.000]); data.push([1400976020000,0.000]); data.push([1400976022000,0.000]); data.push([1400976024000,0.000]); data.push([1400979627000,0.000]); data.push([1400979629000,0.000]); data.push([1400979633000,0.000]); data.push([1400981436000,0.000]); data.push([1400981437000,0.000]); data.push([1400981440000,0.000]); return data; })() }] }); }); }); //]]> </script> </html>
Como podemos ver, los datos a mostrar se introducen directamente. un ejemplo de este datos es:
data.push([1400979633000,0.000]);
El primer dato es el tiempo en milisegundos que han pasado desde el 1 de enero de 1970. El segundo dato, es el valor que queremos mostrar en ese tiempo.
2ºExtraer los datos de la base de datos
Ya hemos varias veces en Geeky Theory Como extraer los datos de una base de datos con PHP, para esto vamos a usar una función que hemos llamado getArraySQL($sql) que nos devolverá un array multidimensional a partir de una sentencia SQL.
function conectarBD(){ $server = "localhost"; $usuario = ""; $pass = ""; $BD = ""; //variable que guarda la conexión de la base de datos $conexion = mysqli_connect($server, $usuario, $pass, $BD); //Comprobamos si la conexión ha tenido exito if(!$conexion){ echo 'Ha sucedido un error inexperado en la conexion de la base de datos<br>'; } //devolvemos el objeto de conexión para usarlo en las consultas return $conexion; } /*Desconectar la conexion a la base de datos*/ function desconectarBD($conexion){ //Cierra la conexión y guarda el estado de la operación en una variable $close = mysqli_close($conexion); //Comprobamos si se ha cerrado la conexión correctamente if(!$close){ echo 'Ha sucedido un error inexperado en la desconexion de la base de datos<br>'; } //devuelve el estado del cierre de conexión return $close; } //Devuelve un array multidimensional con el resultado de la consulta function getArraySQL($sql){ //Creamos la conexión $conexion = conectarBD(); //generamos la consulta if(!$result = mysqli_query($conexion, $sql)) die(); $rawdata = array(); //guardamos en un array multidimensional todos los datos de la consulta $i=0; while($row = mysqli_fetch_array($result)) { //guardamos en rawdata todos los vectores/filas que nos devuelve la consulta $rawdata[$i] = $row; $i++; } //Cerramos la base de datos desconectarBD($conexion); //devolvemos rawdata return $rawdata; }
Para que esto funcione, tenemos que agregar los credenciales del servidor a la función conectarBD(). Una vez hemos implementado este par de funciones, vamos a llamarla a continuación de la siguiente manera:
$sql = "SELECT energy,water,temperature,time from tabla;"; $rawdata = getArraySQL($sql);
$rawdata contendrá un array multidimensional con los valores que le hemos pedido. Podríamos ver este array multidimensional como una tabla donde almacenamos un array de arrays, de tal forma que por ejemplo si quisiéramos obtener la tercera fila del campo energy deberíamos de obtenerlo como $rawdata[2]["energy"].
3º Cambiar el valor del campo time por su equivalencia en milisegundos
Como hemos dicho en el apartado 1 tenemos que añadir los campos en milisegundos. para hacer esto vamos a modificar el array multidimensional obtenido y cambiar el tiempo a milisegundos.
for($i=0;$i<count($rawdata)$i++){ $time = $rawdata[$i]["time"]; $date = new DateTime($time); $rawdata[$i]["time"]=$date->getTimestamp()*1000; }
4º Crear una gráfica dinámica
Una vez que tenemos ya los datos listos para mostrar vamos a sustituir dinámicamente todos los datos dentro del javascript de HighCharts.
<script type='text/javascript'> $(function () { $(document).ready(function() { Highcharts.setOptions({ global: { useUTC: false } }); var chart; $('#container').highcharts({ chart: { type: 'spline', animation: Highcharts.svg, // don't animate in old IE marginRight: 10, events: { load: function() { } } }, title: { text: 'Physical Variables' }, xAxis: { type: 'datetime', tickPixelInterval: 150 }, yAxis: { title: { text: 'Value' }, plotLines: [{ value: 0, width: 1, color: '#808080' }] }, tooltip: { formatter: function() { return '<b>'+ this.series.name +'</b><br/>'+ Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', this.x) +'<br/>'+ Highcharts.numberFormat(this.y, 2); } }, legend: { enabled: true }, exporting: { enabled: true }, series: [{ name: 'Energy Consumption', data: (function() { var data = []; <?php for($i = 0 ;$i<count($rawdata);$i++){ ?> data.push([<?php echo $rawdata[$i]["time"];?>,<?php echo $rawdata[$i]["energy"];?>]); <?php } ?> return data; })() },{ name: 'Water Consumption', data: (function() { var data = []; <?php for($i = 0 ;$i<count($rawdata);$i++){ ?> data.push([<?php echo $rawdata[$i]["time"];?>,<?php echo $rawdata[$i]["water"];?>]); <?php } ?> return data; })() },{ name: 'Temperature', data: (function() { var data = []; <?php for($i = 0 ;$i<count($rawdata);$i++){ ?> data.push([<?php echo $rawdata[$i]["time"];?>,<?php echo $rawdata[$i]["temperature"];?>]); <?php } ?> return data; })() }] }); }); }); </script> Podemos observar que hemos creado tres bucles for para sustituir los datos en cada serie y crear de forma dinámica el javascript con los datos de salida.
Código completo
A continuación podéis encontrar el código completo del tutorial, listo para usar. Hay que tener en cuenta que tenemos que incluir los credenciales de la base de datos y llamar a los campos que queramos mostrar. <?php function conectarBD(){ $server = "localhost"; $usuario = ""; $pass = ""; $BD = ""; //variable que guarda la conexión de la base de datos $conexion = mysqli_connect($server, $usuario, $pass, $BD); //Comprobamos si la conexión ha tenido exito if(!$conexion){ echo 'Ha sucedido un error inexperado en la conexion de la base de datos<br>'; } //devolvemos el objeto de conexión para usarlo en las consultas return $conexion; } /*Desconectar la conexion a la base de datos*/ function desconectarBD($conexion){ //Cierra la conexión y guarda el estado de la operación en una variable $close = mysqli_close($conexion); //Comprobamos si se ha cerrado la conexión correctamente if(!$close){ echo 'Ha sucedido un error inexperado en la desconexion de la base de datos<br>'; } //devuelve el estado del cierre de conexión return $close; } //Devuelve un array multidimensional con el resultado de la consulta function getArraySQL($sql){ //Creamos la conexión $conexion = conectarBD(); //generamos la consulta if(!$result = mysqli_query($conexion, $sql)) die(); $rawdata = array(); //guardamos en un array multidimensional todos los datos de la consulta $i=0; while($row = mysqli_fetch_array($result)) { //guardamos en rawdata todos los vectores/filas que nos devuelve la consulta $rawdata[$i] = $row; $i++; } //Cerramos la base de datos desconectarBD($conexion); //devolvemos rawdata return $rawdata; } //Sentencia SQL $sql = "SELECT energy,water,temperature,time from tabla;"; //Array Multidimensional $rawdata = getArraySQL($sql); //Adaptar el tiempo for($i=0;$i<count($rawdata);$i++){ $time = $rawdata[$i]["time"]; $date = new DateTime($time); $rawdata[$i]["time"]=$date->getTimestamp()*1000; } ?> <HTML> <BODY> <meta charset="utf-8"> <!-- Latest compiled and minified JavaScript --> <script src="https://code.jquery.com/jquery.js"></script> <!-- Importo el archivo Javascript de Highcharts directamente desde su servidor --> <script src="http://code.highcharts.com/stock/highstock.js"></script> <script src="http://code.highcharts.com/modules/exporting.js"></script> <div id="container"> </div> <script type='text/javascript'> $(function () { $(document).ready(function() { Highcharts.setOptions({ global: { useUTC: false } }); var chart; $('#container').highcharts({ chart: { type: 'spline', animation: Highcharts.svg, // don't animate in old IE marginRight: 10, events: { load: function() { } } }, title: { text: 'Physical Variables' }, xAxis: { type: 'datetime', tickPixelInterval: 150 }, yAxis: { title: { text: 'Value' }, plotLines: [{ value: 0, width: 1, color: '#808080' }] }, tooltip: { formatter: function() { return '<b>'+ this.series.name +'</b><br/>'+ Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', this.x) +'<br/>'+ Highcharts.numberFormat(this.y, 2); } }, legend: { enabled: true }, exporting: { enabled: true }, series: [{ name: 'Energy Consumption', data: (function() { var data = []; <?php for($i = 0 ;$i<count($rawdata);$i++){ ?> data.push([<?php echo $rawdata[$i]["time"];?>,<?php echo $rawdata[$i]["energy"];?>]); <?php } ?> return data; })() },{ name: 'Water Consumption', data: (function() { var data = []; <?php for($i = 0 ;$i<count($rawdata);$i++){ ?> data.push([<?php echo $rawdata[$i]["time"];?>,<?php echo $rawdata[$i]["water"];?>]); <?php } ?> return data; })() },{ name: 'Temperature', data: (function() { var data = []; <?php for($i = 0 ;$i<count($rawdata);$i++){ ?> data.push([<?php echo $rawdata[$i]["time"];?>,<?php echo $rawdata[$i]["temperature"];?>]); <?php } ?> return data; })() }] }); }); }); </script> </html> Saludos!