Ir al contenido

Guía de inicio rápido de NextPDF para Symfony

El flujo consiste en inyectar PdfFactory, construir un Document y devolverlo mediante PdfResponse. Para generar un PDF en segundo plano, se despacha un GeneratePdfMessage a un transporte de Messenger.

Se inyecta NextPDF\Symfony\Service\PdfFactory. Su método create() devuelve un NextPDF\Core\Document nuevo. Los valores predeterminados configurados ya se aplican automáticamente: el creador, el autor y el idioma. Ese documento se devuelve mediante NextPDF\Symfony\Http\PdfResponse.

src/Controller/InvoiceController.php
<?php
declare(strict_types=1);
namespace App\Controller;
use NextPDF\Symfony\Http\PdfResponse;
use NextPDF\Symfony\Service\PdfFactory;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
final class InvoiceController
{
#[Route('/invoice/{number}', name: 'invoice_pdf')]
public function download(PdfFactory $pdf, string $number): Response
{
$doc = $pdf->create();
$doc->addPage();
$doc->cell(0, 10, "Invoice #{$number}", newLine: true);
$doc->cell(0, 10, 'Thank you for your business.');
return PdfResponse::download($doc, "invoice-{$number}.pdf");
}
}

PdfResponse::download() devuelve una Symfony\Component\HttpFoundation\Response. Incluye Content-Type: application/pdf, una disposición attachment, un Content-Length y los encabezados de seguridad fijos del bundle. Symfony documenta la clase canónica Response y su modelo de encabezados (https://symfony.com/doc/current/components/http_foundation.html).

Para que el navegador muestre el PDF en lugar de descargarlo, usar inline():

return PdfResponse::inline($doc, 'preview.pdf');

La disposición pasa a ser inline. El resto de los encabezados se mantiene igual.

Para documentos grandes, las variantes de transmisión emiten el PDF en fragmentos de 64 KB. Esto reduce el uso máximo de memoria. Devuelven una Symfony\Component\HttpFoundation\StreamedResponse y omiten Content-Length.

return PdfResponse::streamDownload($doc, 'annual-report.pdf');

streamInline() es el equivalente en línea. Symfony documenta el contrato del callback de StreamedResponse: un callable void que escribe la salida (https://symfony.com/doc/current/components/http_foundation.html).

Paso 4 — Generar un PDF de forma asíncrona

Sección titulada «Paso 4 — Generar un PDF de forma asíncrona»

Cuando symfony/messenger está instalado, se puede trasladar la generación fuera del hilo de la solicitud.

Implementar NextPDF\Symfony\Message\PdfBuilderInterface. El handler pasa al builder un Document nuevo y preconfigurado. También le pasa el contexto serializable del mensaje.

src/Pdf/InvoicePdfBuilder.php
<?php
declare(strict_types=1);
namespace App\Pdf;
use NextPDF\Core\Document;
use NextPDF\Symfony\Message\PdfBuilderInterface;
final class InvoicePdfBuilder implements PdfBuilderInterface
{
public function build(Document $document, array $context): Document
{
$document->addPage();
$document->setFont('dejavusans', '', 12);
$document->cell(0, 10, 'Invoice #' . $context['invoice_id']);
return $document;
}
}

4b — Registrar el builder en el localizador

Sección titulada «4b — Registrar el builder en el localizador»

El handler resuelve los builders desde un localizador de servicios PSR-11 indexado por nombre de clase. Por eso, solo son accesibles los builders registrados. Agregar el builder a un localizador en config/services.yaml:

services:
App\Pdf\InvoicePdfBuilder: ~
nextpdf.pdf_builder_locator:
class: Symfony\Component\DependencyInjection\ServiceLocator
arguments:
- 'App\Pdf\InvoicePdfBuilder': '@App\Pdf\InvoicePdfBuilder'
tags: ['container.service_locator']
NextPDF\Symfony\Message\GeneratePdfHandler:
arguments:
$builderLocator: '@nextpdf.pdf_builder_locator'

El handler solicita al localizador el builder por su id de tipo class-string. Un identificador de contenedor PSR-11 es una cadena que identifica de forma única una entrada (PSR-11 §1.1.2).

Se inyecta Symfony\Component\Messenger\MessageBusInterface y luego se despacha el mensaje:

src/Controller/ReportController.php
<?php
declare(strict_types=1);
namespace App\Controller;
use App\Pdf\InvoicePdfBuilder;
use NextPDF\Symfony\Message\GeneratePdfMessage;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Routing\Attribute\Route;
final class ReportController
{
#[Route('/invoice/{id}/queue', name: 'invoice_queue')]
public function queue(MessageBusInterface $bus, int $id): Response
{
$bus->dispatch(new GeneratePdfMessage(
builderClass: InvoicePdfBuilder::class,
outputPath: '/var/storage/invoices/' . $id . '.pdf',
builderContext: ['invoice_id' => $id],
));
return new Response('PDF generation queued.', 202);
}
}

GeneratePdfMessage es un DTO readonly. Su constructor rechaza rutas de salida vacías o que no sean .pdf, segmentos de path-traversal, esquemas de stream-wrapper y bytes nulos. También exige que builderClass sea un nombre de clase sintácticamente válido. El handler vuelve a validar la ruta de salida en tiempo de ejecución, antes de escribir. Así, una ruta que era segura al despacharse, pero insegura al consumirse, se sigue rechazando. El atributo #[AsMessageHandler] y el contrato de despacho de MessageBusInterface siguen el modelo estándar de Symfony Messenger (https://symfony.com/doc/current/messenger.html).

4d — Enrutar el mensaje y ejecutar un worker

Sección titulada «4d — Enrutar el mensaje y ejecutar un worker»

En config/packages/messenger.yaml, enrutar el mensaje a un transporte:

framework:
messenger:
transports:
async: '%env(MESSENGER_TRANSPORT_DSN)%'
routing:
NextPDF\Symfony\Message\GeneratePdfMessage: async

Luego ejecutar un worker:

Ventana de terminal
php bin/console messenger:consume async
Ventana de terminal
php bin/console debug:container --tag=container.service_locator
php bin/console messenger:consume async --limit=1 -vv

El primer comando confirma que el localizador de builders está registrado. El segundo comando consume un único mensaje de la cola e imprime el progreso del handler.

  • /integrations/symfony/configuration/ — ajustar los valores predeterminados, las fuentes y el servicio de documentos.
  • /integrations/symfony/production-usage/ — seguridad del worker y transmisión bajo carga.
  • /integrations/symfony/troubleshooting/ — problemas comunes de arranque y de tiempo de ejecución.

Cada fila es una afirmación normativa hecha en esta página, fijada a un reference_id completo de 64 caracteres hexadecimales del corpus SDO restringido. La procedencia (manifiesto del corpus, transporte de recuperación) está en _sidecars/rag-citations.yaml.

EspecificaciónCláusulareference_idAfirmación
PSR-11psr_11_container#1.1.2.p4Contrato de identificador del contenedor has()/get()
  • /integrations/symfony/overview/ — resumen de capacidades.
  • /integrations/symfony/install/ — instalación y registro.
  • /integrations/symfony/integration/ — referencia de cableado de extremo a extremo.