Blog personal

A mi me gusta utilizar para hacer los informes JasperServer, pero he de reconocer que para casos donde no se requiere muchos informes o estos no requieren mucha calidad, la solución que más me gusta es utilizar MS Excel para producir los informes que se requiera en el aplicativo.

La mejor solución para general ficheros Excel desde PHP es OpenOffice phpSpreadsheet, que es la evolución de PHPExcel y que he utilizado en este ejemplo.

He hecho dos informes simples utilizando este producto. Si te gusta Excel y lo controlas sufucientemente para preparar informes, estoy seguro que esta solución va a ser de tu agrado.

Aspectos que he tenido en cuenta:

  • Al utilizar esta librería cuyo tamaño (en fichero ZIP) son 78 MB, para que sea facilmente actualizable y para que no me dificulte la generación de desarrollo con PHPRunner la he puesto en un directorio fuera del directorio del aplicativo. Esto tiene muchas ventajas y es una buena forma de trabajar con componentes externos a los que ya nos incorpora PHPRunner.
  • Aunque hay muchas formas de utilizarlo, para cada informe he creado un directorio en «MyCode» en donde está el programa con las funciones y/o métodos que utilizamos del componente, así como una plantilla Excel donde «se describe» el formato del informe. No es necesario utilizar plantillas Excel, pero siempre me ha parecido una forma sencilla y rápida de personalizar los informes.
  • Los informes se lanzar/ejecutan desde botones «custom» de la página de «LIST» de las tablas del modelo de datos.

A modo de ejemplo muestro el código PHP del listado de Municipios:

Ejemplo del informe
<?php
@ini_set("display_errors","1");
@ini_set("display_startup_errors","1");
require_once("../../include/dbcommon.php");

require_once __DIR__ . '/../../../ComponentCode/PhpSpreadsheet/vendor/autoload.php'; 

use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Shared\Date;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\NamedRange;

//  Template processor instance creation

//  $reader = IOFactory::createReader('Xlsx');
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
// $reader->setReadDataOnly(true);
$reader->setIncludeCharts(true);
// $reader->setReadFilter();
// $reader->setReadEmptyCells(true);

$spreadsheet = $reader->load(__DIR__ . '/template.xlsx');

// Variables on different parts of document
$spreadsheet->setActiveSheetIndex(0);

$DateList=now();
$spreadsheet->getActiveSheet()->setCellValue('E2', Date::PHPToExcel($DateList));

// Otra forma de capturar los registros de la BD

$result = $conn->query(
"SELECT m.idrp_municipio, concat(m.CodigoProvincia,' - ',p.NombreProvincia) Provincia, m.CodigoMunicipio, m.NombreMunicipio
FROM rp_municipio m
join rp_provincia p on (m.CodigoProvincia =p.CodigoProvincia) "
);
$data = $result->fetch_all(MYSQLI_NUM); // faster
$dataRows = count($data);

$spreadsheet->setActiveSheetIndex(1);
$spreadsheet->getActiveSheet()->fromArray($data, null, 'A2');

// Define named ranges
$spreadsheet->addNamedRange(new NamedRange('Datos01', $spreadsheet->getActiveSheet(), 'A2:'.'D'.($dataRows-1+2)));

$spreadsheet->setActiveSheetIndex(0);
$spreadsheet->getActiveSheet()->insertNewRowBefore(5, ($dataRows-1)); // Copia formatos de presentación
$spreadsheet->getActiveSheet()->removeRow(($dataRows-1+5), 1); // Eliminar al última

$spreadsheet->addNamedRange(new NamedRange('DatosPrint001', $spreadsheet->getActiveSheet(), 'A3:'.'E'.($dataRows+3)));

// Referenciar los datos
$formulaA4 = $spreadsheet->getActiveSheet()->getCell('A4')->getValue();
$formulaB4 = $spreadsheet->getActiveSheet()->getCell('B4')->getValue();
$formulaC4 = $spreadsheet->getActiveSheet()->getCell('C4')->getValue();
$formulaD4 = $spreadsheet->getActiveSheet()->getCell('D4')->getValue();
$formulaE4 = $spreadsheet->getActiveSheet()->getCell('E4')->getValue();


$dataArray = [];
for ($x = 4; $x <= $dataRows+3; $x++) {
    $dataArray[] = [
                  str_replace("NumRow", $x-3, $formulaA4),
                  str_replace("NumRow", $x-3, $formulaB4),
                  str_replace("NumRow", $x-3, $formulaC4),
                  str_replace("NumRow", $x-3, $formulaD4),
                  str_replace("NumRow", $x-3, $formulaE4)];
} 
$spreadsheet->getActiveSheet()->fromArray($dataArray, null, 'A4'); // Copiar fórmulas

// Romper página por fila
$spreadsheet->setActiveSheetIndex(0);

$Ruptura=$data[0][1];
for ($x = 0; $x <= $dataRows; $x++) {
    if ($Ruptura <> $data[$x][1] ) {
        $Ruptura=$data[$x][1];
        $spreadsheet->getActiveSheet()->setBreak('A'.($x+3), \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_ROW);
    }
}
// $spreadsheet->getActiveSheet()->setBreak('A10', \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_ROW);
// Romper página por Columna
//$spreadsheet->getActiveSheet()->setBreak('C10', \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_COLUMN);
// Show/hide gridlines when printing
$spreadsheet->getActiveSheet()->setShowGridlines(false);

// Config print file Excel
// $spreadsheet->getActiveSheet()->getPageSetup()->setOrientation(\PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_LANDSCAPE);
$spreadsheet->getActiveSheet()->getPageSetup()->setOrientation(\PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_PORTRAIT);
$spreadsheet->getActiveSheet()->getPageSetup()->setPaperSize(\PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::PAPERSIZE_A4);
// Page Setup: Scaling options
$spreadsheet->getActiveSheet()->getPageSetup()->setFitToPage(FALSE);
$spreadsheet->getActiveSheet()->getPageSetup()->setScale(85);
// $spreadsheet->getActiveSheet()->getPageSetup()->setFitToWidth(0); 
// $spreadsheet->getActiveSheet()->getPageSetup()->setFitToHeight(0);

// Page margins
// $spreadsheet->getActiveSheet()->getPageMargins()->setTop(1);
// $spreadsheet->getActiveSheet()->getPageMargins()->setRight(0.6);
// $spreadsheet->getActiveSheet()->getPageMargins()->setLeft(0.6);
// $spreadsheet->getActiveSheet()->getPageMargins()->setBottom(1);
// Center a page horizontally/vertically
$spreadsheet->getActiveSheet()->getPageSetup()->setHorizontalCentered(flase);
$spreadsheet->getActiveSheet()->getPageSetup()->setVerticalCentered(false);
// Setting the print header and footer of a worksheet
// $spreadsheet->getActiveSheet()->getHeaderFooter()->setOddHeader('&C&HPlease treat this document as confidential!');
$spreadsheet->getActiveSheet()->getHeaderFooter()->setOddFooter('&L&B' . $spreadsheet->getProperties()->getTitle() . '&RPágina &P de &N');
// Setting rows/columns to repeat at top/left
$spreadsheet->getActiveSheet()->getPageSetup()->setRowsToRepeatAtTopByStartAndEnd(1, 3);
// Specify printing area
$spreadsheet->getActiveSheet()->getPageSetup()->setPrintArea('A1:'.'E'.($dataRows+3));



// Creación fichero XLSX

// Set active sheet index to the first sheet, so Excel opens this as the first sheet
$spreadsheet->setActiveSheetIndex(0);

// Redirect output to a client’s web browser (Xlsx)
// header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
// header('Content-Disposition: attachment;filename="municipios.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

// Sistema antiguo apoyándose en un fichero temporal
// -------------------- v pie para salvar el nuevo documento  Excel ------------------
$temp_file = tempnam(sys_get_temp_dir(), 'Excel');
// Save EXCEL
// $writer = new Xlsx($spreadsheet);
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
$writer->setPreCalculateFormulas(false); // Desactivar la validación de fórmulas
$writer->save($temp_file);

// ------------------ Operation with file result -------------------------------------------
$documento = file_get_contents($temp_file);
unlink($temp_file);  // delete file tmp
header("Content-Disposition: attachment; filename= Municipios.xlsx");
header('Content-Type: application/excel');
echo $documento;

// Método alternativo, pero también crea un temporal
// $writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
// $writer->save('php://output');
?>

Para desplegar el Proyecto y los Componentes, es necesario que estén al mismo nivel, por ejemplo, como directorios debajo de «htdocs». Siempre podrás modificar esto cambiando el código del ejemplo.