Este es el primer post de muchos que espero hacer sobre trabajar con archivos de Excel u hojas de cálculo con PHP y la librería llamada PHPSpreadsheet.
Lo que veremos hoy será:
Nota: si no te gusta PHP o no quieres realizar la operación desde el servidor, puedes usar JavaScript del lado del cliente con TableExport.
Mira la parte 2 de este tutorial: Leer archivos de Excel con PHP.
Antes de continuar quiero dejar en claro que toda la documentación está aquí. Si algo no queda claro o deseas profundizar en el tema te invito a leerla. El repositorio en GitHub es este.
Necesita la versión de PHP 5.6 o una más nueva, y las siguientes extensiones:
Por cierto, esta es la sucesora de PHPExcel.
Lo más recomendable es usar composer (aquí hay un tutorial de su instalación, y aquí puedes ver por qué usar Composer o cómo adaptarlo a tu proyecto). No importa si ya tenemos un proyecto con composer o si es uno nuevo, abrimos la terminal, nos ponemos en el lugar en donde esté y ejecutamos:
composer require phpoffice/phpspreadsheet
Así como se ve en la imagen:
Luego, en caso de no estar incluyendo el autoload, lo incluimos:
require __DIR__ . "/vendor/autoload.php";
Más tarde indicamos que usaremos algunas clases con espacios de nombres:
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
Y ya podemos comenzar a trabajar.
Crearemos un documento, le pondremos algunas propiedades y veremos las cosas básicas.
<?php
require __DIR__ . "/vendor/autoload.php";
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
$documento = new Spreadsheet();
$documento
->getProperties()
->setCreator("Aquí va el creador, como cadena")
->setLastModifiedBy('Parzibyte') // última vez modificado por
->setTitle('Mi primer documento creado con PhpSpreadSheet')
->setSubject('El asunto')
->setDescription('Este documento fue generado para parzibyte.me')
->setKeywords('etiquetas o palabras clave separadas por espacios')
->setCategory('La categoría');
$writer = new Xlsx($documento);
# Le pasamos la ruta de guardado
$writer->save('nombre_del_documento.xlsx');
Ahí no estamos agregando nada, simplemente creamos un nuevo documento y lo guardamos como nombre_del_documento.xlsx
.
Las propiedades son opcionales, pero las puse para que veamos las posibilidades. En este caso se queda guardado en el mismo lugar en donde ejecutamos el script.
Puede que quieras saber cómo se puede descargar directamente. No te preocupes, la librería ya tiene un ayudante. Veamos cómo crear el documento y hacer que se descargue.
<?php
require __DIR__ . "/vendor/autoload.php";
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
$documento = new Spreadsheet();
$documento
->getProperties()
->setCreator("Aquí va el creador, como cadena")
->setLastModifiedBy('Parzibyte') // última vez modificado por
->setTitle('Mi primer documento creado con PhpSpreadSheet')
->setSubject('El asunto')
->setDescription('Este documento fue generado para parzibyte.me')
->setKeywords('etiquetas o palabras clave separadas por espacios')
->setCategory('La categoría');
$nombreDelDocumento = "Mi primer archivo.xlsx";
/**
* Los siguientes encabezados son necesarios para que
* el navegador entienda que no le estamos mandando
* simple HTML
* Por cierto: no hagas ningún echo ni cosas de esas; es decir, no imprimas nada
*/
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;
En lugar de usar el Writer usamos a IOFactory y mandamos toda la salida a php://output.
Nota sobre los encabezados: sería una larga plática hablar sobre HTTP, pero basta saber que no debemos imprimir otra cosa si queremos que el documento se descargue.
Y cuando digo nada me refiero a NADA, el script debe ejecutarse y salir sin que se imprima otra cosa, ni errores.
Para eso es el exit
al final, para que no cometamos el error de incluir el archivo e imprimamos algo o cosas por el estilo.
Ah, por otro lado el creador dice que si queremos que funcione con internet explorer debemos agregar más encabezados, de manera que quede así:
#<?php
// Redirect output to a client’s web browser (Xlsx)
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="01simple.xlsx"');
header('Cache-Control: max-age=0');
// If you're serving to IE 9, then the following may be needed
header('Cache-Control: max-age=1');
// If you're serving to IE over SSL, then the following may be needed
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); // always modified
header('Cache-Control: cache, must-revalidate'); // HTTP/1.1
header('Pragma: public'); // HTTP/1.0
Finalmente aquí un GIF de cómo se descarga el documento:
Al menos con Windows, así se ve lo que creamos:
Eso nos da a entender que sí está guardando las propiedades.
Recordemos que un documento de estos puede tener muchas hojas. Y cada hoja tiene celdas que tienen una posición, por ejemplo A1, B5, etcétera.
Podemos ver a una hoja como un gigante arreglo bidimensional, o como una tabla.
<?php
require __DIR__ . "/vendor/autoload.php";
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
$documento = new Spreadsheet();
$documento
->getProperties()
->setCreator("Aquí va el creador, como cadena")
->setLastModifiedBy('Parzibyte') // última vez modificado por
->setTitle('Mi primer documento creado con PhpSpreadSheet')
->setSubject('El asunto')
->setDescription('Este documento fue generado para parzibyte.me')
->setKeywords('etiquetas o palabras clave separadas por espacios')
->setCategory('La categoría');
$hoja = $documento->getActiveSheet();
$hoja->setTitle("El título de la hoja");
$hoja->setCellValueByColumnAndRow(1, 1, "Un valor en 1, 1");
$hoja->setCellValue("B2", "Este va en B2");
$hoja->setCellValue("A3", "Parzibyte");
$writer = new Xlsx($documento);
# Le pasamos la ruta de guardado
$writer->save('ejemplo3.xlsx');
Para modificar las celdas debemos obtener primero la hoja, pues un libro de estos se compone de hojas. Una vez que la tenemos, vemos que tenemos dos formas de modificar las celdas.
La primera (setCellValueByColumnAndRow
) es la que recomiendo y prefiero: establecer el valor a partir de la columna y fila; usamos números como coordenadas; eso sí, deben ser a partir del 1, no del 0 como estamos acostumbrados siendo programadores.
El tercer argumento son los datos que le vamos a pasar, los cuales pueden ser de cualquier tipo que pueda mostrar el libro.
Existe una segunda forma y es usar las coordenadas con letras (setCellValue
). Esto sólo recibe 2 argumentos, las coordenadas y los datos.
El nombre o título de la hoja puede ser cambiado opcionalmente con $hoja->setTitle("El título");
, si no lo ponemos se pondrá Worksheet me parece.
Sin importar cómo, al final todos los cambios se guardan y el resultado se ve así:
Simple y poderoso, como todo debe ser.
Esto fue un primer acercamiento a PhpSpreadsheet, pero ya tenemos las bases con las que trabajar. Ya vimos cómo escribir documentos y descargarlos o guardarlos, cosa que omitiremos en los siguientes tutoriales.
A partir de esto ya nos damos una idea de cómo generar cosas más interesantes, porque una vez que tenemos acceso a las celdas podemos modificarlas a nuestro antojo.
Con esta librería podemos generar gráficas, poner fórmulas, establecer contraseñas y obviamente leer documentos, entre otras cosas.
Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…
En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…
En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…
Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…
En este artículo te voy a enseñar cómo usar un "top level await" esperando a…
Ayer estaba editando unos archivos que son servidos con el servidor Apache y al visitarlos…
Esta web usa cookies.
Ver comentarios
Buenas, pues mi duda es que todo el ejemplo me funciona perfecto en mi localhost y cuando lo subo a mi servidor web no funciona para que se descargue el archivo, y ademas si hice la prueba de que lo guarde para ver si esta funcionando la libreria y si me guarda mi archivo que creo de excel en mi servidor web, pero cuando pongo la opcion para que se descargue no se me descarga y mas bien me salen muchos simbolos extraños en mi explorador web.
A mi me pasó lo mismo, yo uso Office 365, y activé la compatibilidad con 2003, y así no me dio errores, después de crear el $writer.
$writer->setOffice2003Compatibility(true);
Por otro lado, he tenido que quitar también las propiedades del documento, se ve que no se cargan bien en Excel 365, y tampoco podía cargar el título ni setCellValueByColumnAndRow.
A partir de ahí fue todo bien
Hola cuando genero el excel me pone que esta dañado pero no me salta ningun error como puedo saber donde fallo
No cargues las propiedades del documento, y no uses el setTitle, ni el setCellValueByColumnAndRow, y cuando crees el $writer, añade en la siguiente línea la compatibilidad con 2003
$writer->setOffice2003Compatibility(true);
Así es como a mí no me daba ningún error al abrirlo
Hola, revisa los logs y desactiva enviar el archivo al final para ver si hay mensajes de error.
Saludos :)
Hola,muy bueno el tutorial. Tengo varias preguntas.
1- Descargué spreadsheet y composer, pero no tengo el archivo autoload.php, tengo que descargarlo de algún lugar ?
2- Utilizas éste código "..require __DIR__ . "/vendor/autoload.php";.." , tengo que crear la carpeta /vendor/ ?
3- Qué archivos irían dentro de esa carpeta ?
4- Debo copiar toda la carpeta Spreadsheet dentro de la carpeta de mi proyecto ?
Muchas gracias.
Hola. Necesitas usar composer, eso responde a tus 4 preguntas; por favor mira los posts que indico para comenzar a usar composer e instalar las dependencias.
Saludos :)
Como pudiera usar phpspreadsheet en dreamweaver de MAC
Me parece que composer está disponible en Mac. Puedes instalarlo para instalar a su vez phpspreadhseet y listo. Luego tendrías que importar el vendor/autoload.php en tu proyecto.
Saludos :)
Hola,
Yo abro un archivo a partir de una plantilla que tiene 5 hojas,
Dependiendo de los datos, necesito eliminar algunas hojas.
Lo estoy haciendo con: $spreadsheet->removeSheetByIndex($nh);
Aunque siempre sale el libro con las hojas que quiero, lo curioso es que en algunos casos me sale error de excel, dice que "hemos encontrado un problema con contenido de . Si confia en el origen de este libro, haga click en Si".
Al dar Si, genera una ventana con errores:
Excel ha completado la validación y reparación en nivel de archivo. Puede que se hayan reparado o descartado algunas partes de este libro.
Parte quitada: Opciones de impresión.
Parte quitada: Forma de dibujo.
El libro queda como yo lo quiero.
Lo que quiero es quitarle estos errores.
Si abro el libro con LibreOffice, no me genera ninguna alerta.
Qué será???
Hola, no lo sé, no me ha pasado, pero lo que se me ocurre es en lugar de eliminar la hoja, crear un nuevo libro y si la hoja sí sirve, la agregas a este nuevo, luego exportas el nuevo y listo.
Es decir, no elimines, mejor crea uno nuevo.
Saludos :)
BUenas tardes, genial tutorial, mi problema es que algo falla en el uso del autolodad y no se define el metodo setCellValue.
Lo instale con composer y cuando pruebo los ejemplos desde:
php -S localhost:8000 -t vendor/phpoffice/phpspreadsheet/samples
funcionan bien.
Hay algo no muy bien con los namespace porque new spreadsheet funciona, pero para IOFactory tengo que usar:
PhpOffice\PhpSpreadsheet\IOFactory::load( $cNombreHojaCalculo );
Alguien sabe que puede pasar? (Lee y graba los arhivos)
pero para instanciar objeto que graba tuve que hacer:
new PhpOffice\PhpSpreadSheet\Writer\Xls( $oPlanilla );
Desde ya muchas gracias.
Hola, en el inicio de tu archivo indica:
use PhpOffice\PhpSpreadsheet\IOFactory;
Haz lo mismo con los otros namespaces.
Saludos :)
Hola Parzibyte, descarga el archivo pero al intentar abrir excel me dice esto = Excel no puede abrir el archivo "nombre_archivo.xlsx" porque el formato o la extension de este no son validos. Compuebe que el archivo no se a dañado y que la extension del mismo coincide con el formato del archivo.
Hola Omar si lo solucione, antes de l save(‘php://output’); debes agregar la siguiente línea de código ” ob_end_clean(); “
Hola amigo solucionaste tu problema, porque justamente tengo el mismo y no se que hacer
Hola amigo. Yo lo acabo de probar y funciona perfectamente, tal vez tienes una versión de Office distinta a la mía o cambiaste alguna parte de los headers o extensiones, recomiendo que verifiques
GRacias por el artirulo!
Lo unico qu eme queda por conseguir es que las fechas tipo "2019-10-23" que seria la de hoy en excel la reconozca como fecha. AUnque ponga formato fecha a la hora de usar filtros despues no discrima entre Año, mes y dia. Alguien sabe como poder hacerlo? :(
GRacias!
Me parece que debes invocar a setFormatCode para que lo tome como fecha, mira la documentación:
https://phpspreadsheet.readthedocs.io/en/latest/topics/recipes/#write-a-date-or-time-into-a-cell
Te invito a seguirme en mis redes sociales y suscribirte a Telegram y YouTube
Saludos
Buenas tardes. Amigo, eso no funca. No me descarga anda. Me sale un error de PHP. "Esta página no funciona". ¿Qué hago?
Hola, buen día. ¿A qué te refieres con "eso"? si te sale esa página es porque se está causando un error de servidor y no se está mostrando. Analiza los mensajes o logs de error, y trata de ver si tu código no tiene errores, ¿podrías compartirlo en un gist para analizarlo?
Saludos