Guía para desarrolladores de Laravel
De un vistazo
Sección titulada «De un vistazo»El paquete de Laravel adapta NextPDF a las convenciones de Laravel sin alterar el ciclo de vida del documento del núcleo. El contenedor gestiona los registros y las factorías compartidos. Cada documento PDF es desechable y debe construirse, devolverse, transmitirse o guardarse una sola vez.
Usar esta guía al diseñar servicios de aplicación, jobs en cola, flujos de respuesta o cobertura de pruebas en torno a nextpdf/laravel.
Límite de la arquitectura
Sección titulada «Límite de la arquitectura»| Capa | Propiedad | Responsabilidad | No incluir aquí |
|---|---|---|---|
| Controlador | Aplicación | Autorizar la solicitud, elegir un constructor de documentos y devolver una respuesta. | Reglas de diseño del PDF compartidas entre casos de uso. |
| Servicio de aplicación | Aplicación | Recopilar los datos de dominio y llamar al código de construcción de documentos. | Lógica de arranque del contenedor o configuración del paquete. |
| Constructor de documentos | Aplicación | Traducir los datos de dominio en llamadas al documento de NextPDF. | Objetos de solicitud, lógica de consultas de Eloquent o detalles del transporte de cola. |
| Integración con Laravel | nextpdf/laravel | Vincular las factorías, los registros, el firmante, el cliente TSA, la facade, las respuestas y el job en cola. | Rutas de almacenamiento específicas del negocio o políticas de inquilinos. |
| Motor del núcleo | nextpdf/nextpdf | Construir y serializar el PDF. | Política de respuesta, cola o sistema de archivos de Laravel. |
Ciclo de vida en tiempo de ejecución
Sección titulada «Ciclo de vida en tiempo de ejecución»| Etapa | Comportamiento | Acción del desarrollador |
|---|---|---|
| Registro del proveedor de servicios | NextPdfServiceProvider::register() registra los registros compartidos, la factoría de documentos, el binding del documento, el cliente HTTP, el cliente TSA, el firmante y los contratos opcionales de facturación electrónica. | Publicar y revisar config/nextpdf.php antes de pasar a producción. |
| Resolución del documento | La facade Pdf y el binding PdfDocumentInterface resuelven un documento nuevo mediante DocumentFactoryInterface. | Resolver un documento una sola vez por solicitud, comando o job en cola. |
| Creación | El código de la aplicación llama a las API del documento del núcleo a través de la facade o del documento inyectado. | Mantener la extracción de datos de dominio fuera del constructor de documentos. |
| Salida terminal | PdfResponse emite la salida HTTP, o el documento se guarda en disco. | Elegir una sola ruta de salida terminal por documento. |
| Ejecución en cola | GeneratePdfJob reconstruye el documento dentro del worker y vuelve a validar la ruta de salida. | Pasar contexto escalar y mantener los callbacks idempotentes. |
Estructura recomendada de la aplicación
Sección titulada «Estructura recomendada de la aplicación»| Ruta | Propósito |
|---|---|
app/Pdf/Builders/* | Constructores de documentos puros. Reciben datos y devuelven un documento completo. |
app/Pdf/Data/* | DTO pequeños que transportan datos de entrada del documento ya autorizados. |
app/Services/* | Orquestación de la aplicación, consultas, traspaso de la autorización y selección de la ruta de almacenamiento. |
app/Jobs/* | Envoltorios opcionales en torno a GeneratePdfJob cuando la aplicación necesita jobs con nombre. |
tests/Feature/Pdf/* | Pruebas de respuesta HTTP, despacho en cola y autorización. |
tests/Unit/Pdf/* | Pruebas de los constructores con datos de entrada pequeños y deterministas. |
Mantener los constructores independientes de los objetos de solicitud de Laravel. Un constructor debe poder invocarse desde un controlador, un comando, una prueba y un worker de cola con la misma entrada.
<?php
namespace App\Pdf\Builders;
use App\Pdf\Data\InvoicePdfData;use NextPDF\Contracts\PdfDocumentInterface;
final readonly class InvoicePdfBuilder{ public function build(PdfDocumentInterface $pdf, InvoicePdfData $data): PdfDocumentInterface { $pdf->setTitle($data->title) ->addPage() ->setFont('dejavusans', '', 12) ->writeHtml($data->html);
return $pdf; }}Patrón de respuesta síncrona
Sección titulada «Patrón de respuesta síncrona»Usar la inyección por constructor cuando el flujo del PDF forme parte de la lógica de la aplicación. Usar la facade solo para flujos cortos de controlador donde el estilo estático mejore la legibilidad.
<?php
namespace App\Http\Controllers;
use App\Pdf\Builders\InvoicePdfBuilder;use App\Pdf\Data\InvoicePdfData;use NextPDF\Contracts\PdfDocumentInterface;use NextPDF\Laravel\Http\PdfResponse;
final readonly class DownloadInvoiceController{ public function __invoke( PdfDocumentInterface $pdf, InvoicePdfBuilder $builder, ) { $document = $builder->build( $pdf, InvoicePdfData::fromInvoiceId(1234), );
return PdfResponse::download($document, 'invoice-1234.pdf'); }}Los helpers de respuesta materializan los bytes del documento antes de construir la respuesta de Laravel. Son helpers de respuesta, no renderers en segundo plano.
Patrón de cola
Sección titulada «Patrón de cola»GeneratePdfJob acepta un callable de construcción y una ruta de salida. El job valida las rutas inseguras durante la ejecución. Aun así, el código de la aplicación debe elegir una raíz de almacenamiento segura para el inquilino antes del despacho.
<?php
use App\Pdf\Builders\QueuedInvoiceBuilder;use NextPDF\Laravel\Jobs\GeneratePdfJob;
GeneratePdfJob::dispatch( outputPath: storage_path('app/pdfs/invoice-1234.pdf'), builder: [QueuedInvoiceBuilder::class, 'build'],)->onQueue(config('nextpdf.queue.queue', 'pdf'));Los callbacks de cola deben ser pequeños. Es preferible escribir el estado duradero desde un listener de jobs de la aplicación, en lugar de almacenar closures complejas en el payload de la cola.
Puntos de extensión
Sección titulada «Puntos de extensión»| Punto de extensión | Uso | Restricción |
|---|---|---|
PdfDocumentInterface binding | Reemplazar o decorar la creación de documentos para los valores predeterminados de toda la aplicación. | Debe devolver una instancia de documento nueva. |
DocumentFactoryInterface | Crear explícitamente un documento nuevo en servicios y pruebas. | No almacenar en caché los documentos devueltos. |
config/nextpdf.php | Valores predeterminados, ajustes de la cola, ajustes del renderer de Chrome, hooks de firma, TSA, caché de OCSP. | Tratar las variables de entorno como configuración de despliegue, no como entrada de la solicitud. |
GeneratePdfJob constructor | Construir documentos de forma asíncrona. | El callable debe ser serializable por el transporte de cola de Laravel. |
| Callbacks de éxito/error | Notificación o limpieza posterior a la generación. | Mantener los callbacks idempotentes y conscientes de los efectos secundarios. |
| Contratos opcionales de Premium | Embebedor de facturas electrónicas, validador, perfil y ejecutor de Schematron. | Resolverlos solo donde el paquete opcional esté instalado y con licencia. |
Flujo de trabajo de desarrollo
Sección titulada «Flujo de trabajo de desarrollo»- Construir el primer documento de forma síncrona en un controlador o una prueba de funcionalidad.
- Mover el código de diseño a una clase de constructor dentro de
app/Pdf/Builders. - Mover la lógica de consultas y autorización a un servicio de aplicación.
- Agregar pruebas de
PdfResponsepara las cabeceras y los nombres de archivo. - Mover la generación lenta o de gran volumen a
GeneratePdfJob. - Agregar pruebas de cola para el contexto serializado, la política de ruta de salida y el manejo de errores.
- Medir la memoria y el tiempo de renderizado con datos de producción representativos.
Manejo de errores
Sección titulada «Manejo de errores»| Error | Dónde debe manejarse | Respuesta recomendada |
|---|---|---|
| Solicitud no válida o documento no autorizado | Controlador o política. | Devolver la respuesta normal de autorización o validación de la aplicación. |
| Fuente faltante o imagen no válida | Pruebas del constructor y registro de la aplicación. | Hacer fallar la solicitud o el job; no emitir PDF parciales. |
| Ruta de salida insegura | Servicio de almacenamiento de la aplicación y GeneratePdfJob. | Rechazar antes del despacho y apoyarse en la validación del lado del worker como defensa en profundidad. |
| Error de firma o de TSA | Límite del servicio de firma. | Decidir si el documento puede quedar sin firmar; de forma predeterminada, fallar de forma cerrada para los documentos regulados. |
| Tiempo de espera de la cola | Configuración del worker de la cola y observabilidad. | Reintentar solo cuando el constructor sea determinista y la ruta de salida sea segura para sobrescribir. |
Valores predeterminados seguros
Sección titulada «Valores predeterminados seguros»| Aspecto | Predeterminado | Cuándo sobrescribir |
|---|---|---|
| Nombre de la cola | pdf | Usar una cola dedicada cuando la generación compita con jobs de cara al usuario. |
| Tiempo de espera del job | 120 segundos | Aumentarlo solo después de medir el tamaño del documento y la capacidad del worker. |
| Nombre de archivo de la respuesta | document.pdf | Usar identificadores de negocio saneados. |
| Registro de fuentes | Compartido y bloqueado tras el calentamiento. | Agregar preload_fonts para las fuentes usadas en rutas calientes. |
| Registro de imágenes | Caché compartida y acotada. | Reducir image_cache_mb para los workers con memoria limitada. |
| Fragmentación de la respuesta transmitida | Fragmentos de 64 KB. | No depender de los límites de los fragmentos; son un detalle de la salida. |
Lista de verificación de pruebas
Sección titulada «Lista de verificación de pruebas»- Las pruebas del controlador verifican
Content-Type,Content-Dispositiony las cabeceras defensivas. - Las pruebas del constructor usan DTO deterministas y no consultan la base de datos.
- Las pruebas de cola verifican que el constructor recibe un documento nuevo.
- Las pruebas de rutas cubren el rechazo de traversal, stream-wrapper, byte nulo y rutas que no son
.pdf. - Las pruebas del worker renderizan documentos representativos con el mismo límite de memoria que en producción.
- Las pruebas de firma opcionales cubren el certificado faltante, la contraseña no válida, la TSA no disponible y el nivel de firma configurado.