php

Consulta de MySQL a CSV y Excel con 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.

Estructura de la tabla

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);

Funciones

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.

Generar reporte como HTML

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"]

Reporte MySQL como HTML – Listo para exportar como CSV o Excel

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.

Generando CSV a partir de consulta SQL

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.

CSV generado a partir de reporte MySQL

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.

Excel a partir de consulta con MySQL

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");

Explicando setCellValue

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.

Recorrer filas de SQL y escribirlas en Excel

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.

Descargar Excel

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:

Archivo de Excel generado con consulta SQL de MySQL usando PHP

Poniendo todo junto

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.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Entradas recientes

Creador de credenciales web – Aplicación gratuita

Hoy te voy a presentar un creador de credenciales que acabo de programar y que…

5 días hace

Desplegar PWA creada con Vue 3, Vite y SQLite3 en Apache

Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…

2 semanas hace

Arquitectura para wasm con Go, Vue 3, Pinia y Vite

En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…

2 semanas hace

Vue 3 y Vite: crear PWA (Progressive Web App)

En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…

2 semanas hace

Errores de Comlink y algunas soluciones

Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…

2 semanas hace

Esperar promesa para inicializar Store de Pinia con Vue 3

En este artículo te voy a enseñar cómo usar un "top level await" esperando a…

2 semanas hace

Esta web usa cookies.