Archivo de Excel generado con consulta SQL de MySQL usando PHP
En este post de programación con PHP y MySQL vamos a ver cómo exportar los resultados de una consulta SQL como CSV (valores separados por coma) y como archivo Excel, además de mostrarlos como HTML.
A partir de las filas que sean devueltas por una consulta vamos a crear un archivo CSV y un archivo de Excel.
En este ejemplo vamos a trabajar con una tabla llamada personas
, cuyas columnas están descritas a continuación. En el script que se muestra a continuación también vas a encontrar algunos datos de prueba.
CREATE TABLE IF NOT EXISTS personas(
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
nombre VARCHAR(255) NOT NULL,
edad INTEGER NOT NULL,
altura DECIMAL(9, 2) NOT NULL,
PRIMARY KEY(id)
);
INSERT INTO personas (nombre, edad, altura) VALUES ('Juan Pérez', 30, 175.50);
INSERT INTO personas (nombre, edad, altura) VALUES ('María Gómez', 25, 160.75);
INSERT INTO personas (nombre, edad, altura) VALUES ('Carlos Ruiz', 40, 180.25);
INSERT INTO personas (nombre, edad, altura) VALUES ('Ana Torres', 22, 165.30);
INSERT INTO personas (nombre, edad, altura) VALUES ('Luis Fernández', 35, 170.10);
INSERT INTO personas (nombre, edad, altura) VALUES ('Carmen Díaz', 28, 168.80);
INSERT INTO personas (nombre, edad, altura) VALUES ('Jorge Martínez', 33, 172.50);
INSERT INTO personas (nombre, edad, altura) VALUES ('Laura Jiménez', 27, 162.45);
INSERT INTO personas (nombre, edad, altura) VALUES ('Marta Sánchez', 31, 169.60);
INSERT INTO personas (nombre, edad, altura) VALUES ('Pedro López', 29, 174.90);
Comencemos viendo el código PHP que va a tener una función para obtener la base de datos y una que realiza la consulta SQL y devuelve las filas de la tabla:
<?php
function obtenerBaseDeDatos()
{
$contraseña = "";
$usuario = "root";
$nombre_base_de_datos = "personas";
$base_de_datos = new PDO('mysql:host=localhost;dbname=' . $nombre_base_de_datos, $usuario, $contraseña);
$base_de_datos->query("set names utf8;");
$base_de_datos->setAttribute(PDO::ATTR_EMULATE_PREPARES, FALSE);
$base_de_datos->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
#$base_de_datos->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
return $base_de_datos;
}
function obtenerPersonas()
{
$bd = obtenerBaseDeDatos();
$sentencia = $bd->query("SELECT id, nombre, edad, altura FROM personas");
return $sentencia->fetchAll(PDO::FETCH_ASSOC);
}
Tenemos a la función que obtiene la base de datos. Debes configurar las credenciales de acceso y el nombre de la base de datos. En mi caso específico la base de datos se llama personas
, y he creado una tabla con el mismo nombre dentro de esa base de datos. Puede que en tu caso el usuario, contraseña o nombre de la base de datos sea distinto.
Por otro lado, la segunda función devuelve la lista de personas haciendo la consulta SQL e invocando a la función que obtiene la base de datos. Esta función será usada en los 3 casos: al mostrar los datos como HTML, como CSV y como Excel. La única diferencia radica en el formato de los datos.
En el primer caso vamos a ver cómo mostrar los resultados de la consulta en el navegador web para que el usuario pueda revisarlos. Simplemente invocamos a la función y dibujamos una tabla HTML recorriendo el arreglo de personas.
Para lograr esto, estoy incluyendo el archivo funciones.php
que contiene las 2 funciones necesarias:
<?php
include_once "funciones.php";
$personas = obtenerPersonas();
?>
Luego, estoy dibujando la tabla:
<table>
<thead>
<tr>
<th>ID</th>
<th>Nombre</th>
<th>Edad</th>
<th>Altura</th>
</tr>
</thead>
<tbody>
<?php foreach ($personas as $persona) { ?>
<tr>
<td><?php echo $persona["id"]; ?></td>
<td><?php echo $persona["nombre"]; ?></td>
<td><?php echo $persona["edad"]; ?></td>
<td><?php echo $persona["altura"]; ?></td>
</tr>
<?php } ?>
</tbody>
</table>
Estoy recorriendo todas las filas devueltas por la consulta en el foreach
. Después, en cada paso, dibujo una nueva celda con la etiqueta td
y accedo a cada propiedad usando la notación []
, por ejemplo, para acceder a la edad de la persona acceso con $persona["edad"]
Fíjate en que ahí mismo tengo los botones para descargar el reporte de MySQL como CSV o como Excel. Lo importante es que sepas que voy a usar la misma función para generar esos 2 archivos.
Veamos el siguiente apartado en donde simplemente concatenamos una cadena y vamos agregando los valores separados por coma. Estamos usando un foreach
al igual que en la ocasión anterior, pero ahora en cada paso agregamos contenido a la cadena.
En este caso estoy usando sprintf
para evitar concatenaciones confusas y para tener un código más limpio.
Después, simplemente muestro la cadena como respuesta a la petición del cliente, pero antes de ello establezco los encabezados para que el navegador sepa que no debe mostrar el contenido, sino forzar su descarga como archivo CSV:
<?php
include_once "funciones.php";
$personas = obtenerPersonas();
$nombreArchivoSalida = "personas.csv";
$contenidoCsv = "Id,Nombre,Edad,Altura\n";
foreach ($personas as $persona) {
$contenidoCsv .= sprintf(
"%d,%s,%d,%0.2f\n",
$persona["id"],
$persona["nombre"],
$persona["edad"],
$persona["altura"],
);
}
header('Content-Type: application/octet-stream');
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=$nombreArchivoSalida");
echo $contenidoCsv;
Cuando alguien visite el archivo csv.php
se le va a descargar un archivo CSV con todos los datos de la tabla SQL.
Ya lo dije anteriormente pero lo repito: seguimos usando la misma función, por lo que el reporte HTML, CSV y Excel van a tener los mismos datos.
Pasemos al último caso: la generación de un archivo de Excel usando MySQL. En este caso vamos a necesitar la librería PHPSpreadSheet que se instala con:
composer require phpoffice/phpspreadsheet
Cuando descargues el código completo simplemente ejecuta: composer install
en la ubicación raíz del proyecto, eso va a crear una carpeta llamada vendor
en donde estarán todas las dependencias.
Luego incluimos esas clases en el archivo que se encarga de generar el reporte, así como nuestras propias funciones:
<?php
require __DIR__ . "/vendor/autoload.php";
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
include_once "funciones.php";
$personas = obtenerPersonas();
Después creamos un nuevo documento y accedemos a la primer hoja, aprovechando también para colocar el título de la misma:
$documento = new Spreadsheet();
$documento
->getProperties()
->setTitle('Reporte de personas');
$hoja = $documento->getActiveSheet();
$hoja->setTitle("Personas");
Es necesario hacer una explicación adicional para el caso de setCellValue. En este caso la vamos a invocar con la siguiente sintaxis:
setCellValue([$indiceColumna, $indiceFila], "valor");
El índice de columna es el equivalente a las letras en Excel, de modo que la A es la columna 1. El índice de fila es el número de fila de Excel, de modo que el 1 es la primer fila. Así que si invocamos a:
setCellValue([3,5], "Luis");
Sería el equivalente a colocar el valor Luis
en la celda C5
usando Excel.
En este caso voy a usar algunas banderas de índices para llevar un seguimiento de la columna y fila actual, ya sea al momento de escribir los encabezados de la tabla o al momento de agregar cada persona al documento.
Comenzamos iniciando las variables ayudantes para los índices de filas y columnas. Después, escribimos el encabezado:
$indiceFila = 1;
$indiceColumna = 1;
$columnas = ["id", "nombre", "edad", "altura"];
$encabezados = ["Id", "Nombre", "Edad", "Altura"];
foreach ($encabezados as $encabezado) {
$hoja->setCellValue([$indiceColumna, $indiceFila], $encabezado);
$indiceColumna++;
}
$indiceFila++;
Después recorremos el arreglo de personas, mismo que será devuelto por la función que vimos desde el principio.
foreach ($personas as $persona) {
$indiceColumna = 1;
foreach ($columnas as $columna) {
$hoja->setCellValue([$indiceColumna, $indiceFila], $persona[$columna]);
// La edad va como número
if ($indiceColumna === 3) {
$hoja->getStyle([$indiceColumna, $indiceFila])->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_NUMBER);
}
// La altura va con decimales
if ($indiceColumna === 4) {
$hoja->getStyle([$indiceColumna, $indiceFila])->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_NUMBER_00);
}
$indiceColumna++;
}
$indiceFila++;
}
Fíjate en que estoy accediendo a $persona[$columna]
, esto es una manera simple de dibujar todas las columnas. Podría hacerse con $persona["id"]
, después con $persona["nombre"]
y así sucesivamente, pero es más fácil definir un arreglo de las propiedades que tiene cada fila devuelta por MySQL y usar dicho valor como clave.
En algunos casos estoy definiendo el formato, ya que los números y flotantes deben ser definidos como lo que son. Para ello, reviso el índice de la columna de personas en el que estoy, y si es el 3 (que corresponde a la edad) o el 4 (altura) agrego el formato con setFormatCode
.
Cuando terminemos de recorrer la lista de personas vamos a tener el documento con todos los datos, pero resta enviarlo como respuesta al cliente. Para ello veamos el siguiente código:
$nombreDelDocumento = "Reporte de personas.xlsx";
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="' . $nombreDelDocumento . '"');
header('Cache-Control: max-age=0');
$writer = IOFactory::createWriter($documento, 'Xlsx');
$writer->save('php://output');
exit;
Cuando el usuario visite el enlace, se le va a descargar el archivo de Excel de manera automática:
Te he mostrado las partes más importantes, pero el código completo está en GitHub. Ahí vas a encontrar la definición de la tabla, el archivo composer.json
y los archivos completos para generar HTML, CSV y Excel usando PHP y MySQL: https://github.com/parzibyte/mysql-csv-excel-php
Cuando lo descargues, instala las dependencias con composer install
, configura la base de datos en funciones.php
y asegúrate de crear la tabla como se indica en esquema.sql
.
Recuerda: si hay algún error, el mensaje de error contendrá los detalles necesarios para arreglarlo. El código está probado y funciona perfectamente, solo que en ocasiones es necesario habilitar extensiones o actualizar la versión de PHP.
En este post te quiero compartir un código de C++ para listar y cancelar trabajos…
Gracias a WebAssembly podemos ejecutar código de otros lenguajes de programación desde el navegador web…
Revisando y buscando maneras de imprimir un PDF desde la línea de comandos me encontré…
Esta semana estuve recreando la API del plugin para impresoras térmicas en Android (HTTP a…
Hoy te enseñaré a extraer la cadena base64 de una clave PEM usando una función…
Encender un foco con un Bot de Telegram es posible usando una tarjeta como la…
Esta web usa cookies.