Uso en producción del paquete de NextPDF para Laravel
De un vistazo
Sección titulada «De un vistazo»En producción, resolver el contrato del documento mediante inyección por constructor. Gestionar los fallos de escritura de PDF con una excepción específica. Mover la generación pesada o por lotes a GeneratePdfJob y conectar retrollamadas explícitas de éxito y de fallo.
Instalación
Sección titulada «Instalación»composer require nextpdf/laravelphp artisan vendor:publish --tag=nextpdf-configConfigurar la conexión de cola en config/nextpdf.php. Definir queue.connection, queue.queue y queue.timeout. A continuación, asegurarse de que haya un worker en ejecución para la conexión configurada.
Panorama conceptual
Sección titulada «Panorama conceptual»El contenedor expone NextPDF\Contracts\PdfDocumentInterface como un enlazado de fábrica. Cada resolución produce un nuevo NextPDF\Core\Document. PSR-11 permite que un contenedor devuelva valores distintos en llamadas sucesivas a get(), según la estrategia de enlazado (PSR-11 §1.1.2). Aquí, el paquete usa un enlazado de fábrica para que el estado mutable con alcance de petición nunca se comparta entre peticiones. Los registros de fuentes y de imágenes son singletons. Esto mantiene el contrato de que un identificador enlazado se resuelve a su entrada registrada (PSR-11 §1.1.2), al tiempo que comparte los recursos costosos en todo el worker.
Preferir la inyección por constructor frente a la facade en el código de producción. Esto hace explícita la dependencia y mantiene el controlador apto para pruebas unitarias sin arrancar la raíz de la facade.
Ejemplo de código — Producción
Sección titulada «Ejemplo de código — Producción»Controlador con inyección de dependencias y manejo tipado de errores
Sección titulada «Controlador con inyección de dependencias y manejo tipado de errores»<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use Illuminate\Http\Response;use NextPDF\Contracts\PdfDocumentInterface;use NextPDF\Laravel\Http\PdfResponse;use Psr\Log\LoggerInterface;use Throwable;
final class InvoiceController extends Controller{ public function __construct( private readonly PdfDocumentInterface $document, private readonly LoggerInterface $logger, ) {}
public function show(int $invoiceId): Response { try { $this->document->addPage(); $this->document->cell(0, 10, "Invoice #{$invoiceId}", newLine: true); $this->document->cell(0, 10, 'Thank you for your business.');
return PdfResponse::download( $this->document, "invoice-{$invoiceId}.pdf", ); } catch (Throwable $exception) { // Rethrow as an HTTP-meaningful failure; never swallow. $this->logger->error('Invoice PDF generation failed', [ 'invoice_id' => $invoiceId, 'exception' => $exception::class, ]);
return new Response('Could not generate the invoice PDF.', 500); } }}Inyectar PdfDocumentInterface, no el Document concreto, para que el enlazado siga siendo intercambiable en las pruebas. El contenedor devuelve un documento nuevo en cada instanciación del controlador. No reutilizar la misma instancia de controlador para dos documentos sin relación dentro de un mismo proceso.
El bloque catch registra la clase de la excepción y devuelve un error HTTP definido en lugar de filtrar una traza de pila. Usar Psr\Log\LoggerInterface, que el contenedor resuelve como el logger del framework. PSR-3 deja el escape de marcadores de posición en manos de quien implementa e indica a quienes llaman que no escapen previamente los valores de contexto (PSR-3 §1.2). Pasar contexto estructurado, no cadenas interpoladas.
Generación encolada con retrollamadas de éxito y de fallo
Sección titulada «Generación encolada con retrollamadas de éxito y de fallo»GeneratePdfJob es un trabajo ShouldQueue. De forma predeterminada, usa tres intentos, un tiempo de espera de 120 segundos y un retroceso de 10 segundos. Es posible anular los tres mediante config/nextpdf.php. El closure constructor recibe el documento resuelto por el contenedor y debe devolver un documento configurado.
<?php
declare(strict_types=1);
namespace App\Jobs;
use NextPDF\Contracts\PdfDocumentInterface;use NextPDF\Laravel\Jobs\GeneratePdfJob;use Psr\Log\LoggerInterface;use Throwable;
final class DispatchMonthlyStatement{ public function __construct(private readonly LoggerInterface $logger) {}
public function __invoke(int $accountId): void { // Dispatchable::dispatch() is `public static`: it constructs the // job from the arguments it receives and returns a PendingDispatch. // Pass every constructor argument — including the callbacks — to // the static call. Building an instance and then calling // `$job->dispatch(...)` would discard that instance (and its // callbacks) and queue a different job from only the static args. GeneratePdfJob::dispatch( storage_path("app/statements/{$accountId}.pdf"), static fn (PdfDocumentInterface $document): PdfDocumentInterface => $document ->addPage() ->cell(0, 10, "Statement for account {$accountId}", newLine: true), function (string $path) use ($accountId): void { $this->logger->info('Statement PDF written', [ 'account_id' => $accountId, 'path' => $path, ]); }, function (Throwable $exception) use ($accountId): void { $this->logger->error('Statement PDF failed', [ 'account_id' => $accountId, 'exception' => $exception::class, ]); }, ); }}GeneratePdfJob::dispatch() reenvía sus argumentos directamente al constructor (string $outputPath, callable $builder, ?callable $onSuccess, ?callable $onFailure). Así, las retrollamadas de éxito y de fallo quedan conectadas al mismo trabajo encolado. Esto coincide con la forma posicional GeneratePdfJob::dispatch($path, $builder) de /integrations/laravel/quickstart/. La retrollamada de éxito recibe la ruta de salida y la retrollamada de fallo recibe el Throwable. El trabajo también expone setters fluidos then() y catch(), que devuelven el trabajo para permitir el encadenamiento. Usar esos setters solo cuando se conserve y se despache esa misma instancia, por ejemplo mediante el helper dispatch(). El trabajo también expone un método failed(), que el ejecutor de cola invoca ante un fallo terminal. Las retrollamadas se envuelven en closures serializables para que sobrevivan al transporte de la cola.
Ajuste del comportamiento de la cola
Sección titulada «Ajuste del comportamiento de la cola»| Propiedad | Predeterminado | Clave de configuración |
|---|---|---|
tries | 3 | no controlado por configuración; crear una subclase para cambiarlo |
timeout | 120 | nextpdf.queue.timeout |
backoff | 10 | no controlado por configuración; crear una subclase para cambiarlo |
| nombre de la cola | pdf | nextpdf.queue.queue |
| conexión | predeterminada | nextpdf.queue.connection |
tries y backoff son propiedades públicas que se leen desde la instancia del trabajo. El trabajo proporcionado no las controla desde la configuración. Para anularlas cuando la política de reintentos sea distinta, se debe crear una subclase de GeneratePdfJob.
Casos límite y trampas
Sección titulada «Casos límite y trampas»- El closure constructor debe devolver un
PdfDocumentInterface. El trabajo guarda ese valor de retorno, no la instancia resuelta originalmente. La prueba del trabajo verifica explícitamente este contrato. - Resolver
SignerInterfacedevuelvenulla menos que la firma esté habilitada, haya un certificado configurado ynextpdf/premiumesté instalado. Comprobar siempre si es null antes de firmar. - Los workers de larga duración (Octane/RoadRunner/Swoole) comparten el registro de fuentes bloqueado. Configurar
preload_fontspara que el calentamiento ocurra una sola vez al arrancar el worker y no en la primera petición. - Un trabajo fallido invoca
failed()tras agotartries. El fallo de un intento individual no llama aonFailurehasta que el ejecutor de cola declara un fallo terminal.
Rendimiento
Sección titulada «Rendimiento»La generación síncrona en el controlador bloquea la petición durante toda la construcción del PDF. Para salidas de varias páginas o por lotes, despachar GeneratePdfJob y responder de inmediato. Los registros singleton amortizan el análisis de fuentes y la decodificación de imágenes a lo largo de la vida del worker. Así, el coste por petición se limita a la construcción del documento y a la emisión del contenido.
Notas de seguridad
Sección titulada «Notas de seguridad»El controlador con inyección de dependencias registra la clase de la excepción, no su mensaje ni su traza, para evitar exponer detalles internos en los logs. GeneratePdfJob valida la ruta de salida en el worker para mitigar cargas serializadas manipuladas durante el transporte de la cola. La cobertura completa se encuentra en /integrations/laravel/security-and-operations/.
Conformidad
Sección titulada «Conformidad»| Afirmación | Fuente | Cláusula | reference_id |
|---|---|---|---|
| El identificador enlazado se resuelve a su entrada registrada | Contenedor PSR-11 | §1.1.2 | |
| Las resoluciones sucesivas pueden diferir según la estrategia de enlazado (enlazado de fábrica) | Contenedor PSR-11 | §1.1.2 |
La guía de logging de PSR-3 está documentada en la especificación PSR-3. Allí se establece que el escape de marcadores de posición es responsabilidad de quien implementa y que quienes llaman pasan contexto estructurado. Consultar el documento psr_3_logger §1.2.
Contexto comercial
Sección titulada «Contexto comercial»La salida firmada PAdES B-B y el archivado PDF/A mediante nextpdf/premium usan la misma superficie de inyección de dependencias. Se trata de una capacidad opcional de Enterprise. El paquete Core que se documenta aquí no necesita cambios de código para adoptarla. Consultar https://nextpdf.dev/get-license/?intent=laravel-signing.
Consulta también
Sección titulada «Consulta también»- /integrations/laravel/quickstart/ — primer ejemplo mínimo
- /integrations/laravel/configuration/ — claves de cola, de firma y de fuentes
- /integrations/laravel/security-and-operations/ — modelo de amenazas y endurecimiento
- /integrations/laravel/troubleshooting/ — fallos comunes en producción