Cómo crear informes PDF en Laravel

Algo con lo que posiblemente tiene que contar nuestros proyectos es la presentación de informes en los que usuario pueda cargar una serie de datos o estadísticas interesantes que pueda ver posteriormente en un documento PDF para su análisis o impresión. Aquí en esta parte del manual, vamos a aprender a cómo crear informes PDF en Laravel 5.

Lo primero de todo es destacar la utilización de Dompdf. Dompdf es un conversor de contenido HTML que es renderizado con PHP para obtener un archivo PDF. No hay palabras que describan mejor la utilidad tan sugerente que supone esto para cualquier desarrollador de aplicaciones.

Instalación de Dompdf vía Composer

Para instalar fácilmente Dompdf en un proyecto de Laravel se recomienda el uso de Composer y para ello desde cualquier consola y dentro del directorio del proyecto ejecutamos la línea de comandos:

composer require barryvdh/laravel-dompdf

Si se ha instalado correctamente tendremos instalada la dependencia dentro de nuestro archivo composer.json:

"require": {
        "php": ">=5.6.4",
        "barryvdh/laravel-dompdf": "^0.8.0", //<-HERE
        "doctrine/dbal": "^2.5",
        "guzzlehttp/guzzle": "^6.3",
        "laravel/framework": "5.4.*",
        "laravel/tinker": "~1.0",
        "laravelcollective/html": "^5.4"
    }

Sería recomendable que lanzáramos también el comando de actualización de composer para que se pongan al día todas las dependencias.

Una vez instalado tenemos que declarar los Providers para su utilización en Laravel. Estos providers o clases se agregan en el archivo /config/app.php en el array de los providers y agregamos también un alias para el espacio de nombres:

'providers' => [
   // Laravel Framework Service Providers...
   Barryvdh\DomPDF\ServiceProvider::class,
],
'aliases' => [
    //rest of aliases
   'PDF' => Barryvdh\DomPDF\Facade::class,
],

Notación importante sobre las fuentes

Dompdf requiere la instalación de fuentes externas para generar los documentos PDF. Lo primero que tenemos que configurar es un directorio de instalación para estas fuentes. Por defecto, siempre será /storage/fonts. Aquí Dompdf podrá instalar automáticamente las fuentes que requiera, incluso si nosotros queremos usar las nuestras propias también deben ir incluidas aquí.

El autor recomienda además, la utilización de la fuente DejaVu Sans ya que es una fuente que engloba la gran mayoría de caracteres raros; desde los kenjis japoneses hasta los acentos de los idiomas latinos como el español. Simplemente tendremos que agregarla mediante CSS pero eso se verá más adelante.

Dado que la conversión se hace desde un archivo HTML y en Laravel estamos acostumbrados al uso de plantillas y vistas en PHP, se debe configurar dichas vistas a HTML todo lo que se pueda. Es por eso que lo recomendable es no extender las vistas de archivos de plantilla con sentencias BLADE, pero sobre todo es muy importante añadir el meta:

<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>

Dividir entre controlador y vista

Un buen método de trabajo siempre será el que argumenta la filosofía de MVC. Se puede partir de la instalación de un controlador que maneje las vistas, como la preparación de los datos que se van a enviar a las mismas (que van a ser impresas en PDF). El siguiente controlador puede ser un buen ejemplo:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Usuario;
use PDF;

class PDFController extends Controller
{
	public function index(){
	  return view('pdf.listados_informes');
	}

	public function crearPDF($datos, $vista){
          // procesamiento de datos [opcional por si hay que pasarle otra capa de proceso]
	  $data = $datos;
		
	  // generación de la vista
	  $pdf = PDF::loadView( $vista, compact('data') );

	  // lanzamos la descarga del fichero
	  return $pdf->download('informe.pdf');
	}

	public function crearInformeTodosUsuarios(){
	  // preparación de los datos que se van a pasar
          $usuarios = Usuario::all();

          // preparación de la ruta a la vista
          $vistaurl = 'pdf.reporte_todas_revisiones';

          // llamada a la función que genera el PDF
          return $this->crearPDF($usuarios, $vistaurl);
	}
}

Empezando desde arriba, estamos añadiendo el alias ‘PDF’ que habíamos configurado anteriormente. Luego, el controlador tiene una función genérica index() que muestra una vista con los diferentes informes que podemos generar con sus enlaces de descarga.

La función crearInformeTodosUsuarios() se encarga de preparar los datos que le vamos a enviar al generador, donde están todos los usuarios de nuestro sistema y una vista. Esta vista es la que tenemos que estructurar en formato HTML para que el informe PDF luzca idéntico a ella. Y todo ello, se manda a la función crearPDF() donde llamamos al método loadView de la clase PDF. Este método recibe dos argumentos, la vista preparada y los datos que se le van a pasar a la vista (los usuarios). Por último, se lanza el método download que directamente ejecuta la descarga del documento PDF tras procesarse, el cual recibe el argumento del nombre.

<!DOCTYPE html>
<html lang="es">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- Styles -->
  <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" rel="stylesheet">
   <style>
 	body { font-family: DejaVu Sans; }
   </style>
</head>
<body>
  <div class="container">
    <div class="row">
	<table class="table table-bordered">
          <thead>
	    <tr>
		<th>#</th>
		<th>Usuario</th>
	    </tr>
	  </thead>
	  <tbody>
	      @foreach($data as $x)
	        <tr>
	          <td>{{ $x->id }}</td>
	          <td>{{ $x->nombre }}</td>
	        </tr>
	      @endforeach
	      </tbody>
	</table>
    </div>
  </div>
</body>
</html>

Esta podría ser la vista que procesaríamos para que al renderizarla no obtener un archivo PDF plano sin estilos o con caracteres raros. Es por ello, la importancia de agregar en el estilo la fuente deseada. En el proceso de transformación se descargarán las fuentes necesarias y serán instaladas en el proyecto. Además se ha añadido el meta para la conversión utf-8 mencionada anteriormente.

Con todos estos componentes preparados, tan solo se tendría que diseñar los botones de descarga si optamos por el método download en el controlador. Al pulsar sobre ellos se ejecuta la descarga desde el navegador del informe ya listo en PDF.

16 comentarios en «Cómo crear informes PDF en Laravel»

  1. Hola jesus, tu ejemplo me funciona bien pero no puedo resolver cuando recupero los datos de más de 2 tablas y quiero mostrarlos. Ejemplo:
    $ plancuenta = DB :: table (‘plancuenta como pc’)
    -> join (‘plancuentatipo como pct’, ‘pc.idtipo’, ‘=’, ‘pct.idtipo’)
    -> select (‘pc.idplancuenta como cuenta ‘,’ pc.nombre ‘,’ pct.descripcion ‘);
    $ pdf = PDF :: loadView (‘pdf.plancuenta’, [‘plancuenta’ => $ plancuenta]);
    return $ pdf-> download (‘plancuenta.pdf’);
    Se agradeceria si me guiaras. Saludos

    Responder
    • Hola Horacio, primero me aseguraría de que el resultado deseado se te imprime por pantalla tal y como deseas. Quizá el fallo está antes de que salga el PDF resultante.

      Responder
  2. El problema que veo es que para crear el pdf debo ejecutar un «return», lo cual impide que se ejecute cualquier sentencia que hubiese despues.
    Digamos que quiero que se genere el PDF y despues que me redirija a mi pagina principal. ¿Como hago eso?

    Responder
    • Hola Daniel. Todo depende de cómo tengas construido tu petición de informes. Yo por ejemplo, vinculo la función de crear el PDF a un botón de descarga en una vista que me lista las opciones de descarga. Al clicar, me sale la ventana de descarga y continúa en la vista donde nos encontrábamos.
      No podemos mandar un «redirect» después del «return». A no ser que lo hagas a nivel de JavaScript (o lado del cliente). Es la única manera que se me ocurre. Programa un script on(«click», redirección a mi home)

      Responder
    • Hola Alfredo. A ver, sí ya el pdf se guarda cuando te lo descargas.
      Para mandarlo por mail directamente se podría también, siempre que usemos alguna clase para envío de correo.
      Por ejemplo, PHPMailer tiene la propiedad de agregar adjuntos (pdf, fotos, archivos, lo que quieras). El método es AddAttachment(), fíjate en este ejemplo

      Responder
    • Previamente debes de guardar el archivo .PDF generado, por ejemplo (Esto en el controlador):

      $content = $pdf->download()->getOriginalContent();
      Storage::put(‘/pdf/’. ‘nombreArchivo.pdf’,$content) ;

      Luego en el mailable creado para construir el correo vas a enviar, usa el método «attach» de la clase indicándole la ubicación y el nombre del archivo a enviar:

      ->attach(storage_path(‘/app/pdf/’).$this->fileName, [
      ‘as’ => $this->fileName,
      ‘mime’ => ‘application/pdf’,
      ]);

      Espero esto te pueda orientar un poco. Tengas un Happy Coding!

      Responder
  3. Hola Jesus, excelente la explicacion de como usar DomPDF, en Laravel, gracias por compartirlo… estoy recien leyendo sobre el tema porque tengo que hacer algo bastante similar a lo que explicas la unica diferencia, es que no quiero dar la opcion al usuario que descargue el PDF sino que se vea como una vista previa embebido en el navegador. por otra parte veo que usas el metodo
    $pdf->download(‘informe.pdf’);

    Existe alguna manera en la que yo pueda descargar automaticamente el pdf pero que se guarde en el storage de mi aplicacion para luego consultarlo con un iframe para hacer la fucionalidad del embebido, no se si sea la forma mas optima de lograr lo que quiero, pero es la unica manera que me viene a la mente.

    Espero puedas orientarme.

    Saludos, y gracias nuevamente Jesus por compartir tus conocimientos.

    Responder
    • Hola Miguel, desde luego que puedes guardarlo directamente.
      Aquí en la documentación oficial te enseña a cómo guardarlo en un directorio dentro de /public. Laravel además tiene otros motores de almacenamiento como por ejemplo FTP. Pero por lo que me has comentado, sencillamente tienes que generar el documento pdf y a continuación realizarle un push() en tu carpeta public.
      No te olvides antes de generar el link simbólico con artisan: php artisan storage:link
      Gracias por tu comentario y espero haberte sido de ayuda.

      Responder
    • Puede que no sea problema del plugin. Me suena más a que el servidor de PHP está configurado con escaso tiempo de procesamiento. Intenta incrementarle la tasa de memoria máxima y el tiempo de espera en el archivo php.ini

      Responder
  4. Hola! Disculpa genero mi pdf a través de una vista que extiende de un layout y a través de un controlador, similar a lo que tienes excepto que en la vista no tengo las típicas etiquetas de HTML Heard Body ya que si las creo el pdf me lo genera en blanco sin mi contenido.

    Como puedo generar mi pdf correctamente ya que como lo genero los margenes me salen a tope y al imprimir se pierde una parte de la información e intentado agregarle margenes paginacion y cosas asi pero no puedo no las ejecuta ni por configuración en php ni en archivo css?

    Gracias!!

    Responder
    • Hola, gracias por tu consulta.
      Lo primero, disculpas por la demora. Es recomendable que la salida esté maquetada en HTML. No es muy complicado y recuerda que lo que le pasamos a la función es una vista Blade. No va a esperar ningún otro formato. En cuanto al estilo, he actualizado la entrada para que ahora coja la versión más reciente de Bootstrap (podría ser cualquier otro, el que quieras) y las propiedades de estilo deben ir en el HEAD de la plantilla, contenidos dentro de la etiqueta STYLE.
      Es el mejor resultado que me ha dado.

      Responder
  5. Buenas tardes, antes trabajando con la librería DomPDF en laravel, todo me genera bien la descarga y la información, pero mi problema es que quisiera unir o anexar un pdf que ya existe al pdf que se genera.

    Responder
  6. Buen día Jesús.
    Como puedo agregar fuente al pdf, ya intente lo que mencionas en el apartado «Notación importante sobre las fuentes», no logro obtener el resultado, será que me puedas ayudar. Muchas gracias y mucho éxito.

    Responder

Deja un comentario