Ir al contenido

Guía para desarrolladores de Symfony

El paquete de Symfony está orientado a servicios. Inyectar PdfFactory, llamar a create() para cada documento y usar los builders de Messenger para la generación asíncrona. La factory puede ser un servicio del contenedor porque cada llamada devuelve un documento nuevo.

Usar esta guía al diseñar controladores, servicios, handlers de Messenger o puntos de extensión a nivel de bundle en torno a nextpdf/symfony.

CapaPropiedad deResponsabilidadNo incluir aquí
ControladorAplicaciónAutorizar la solicitud, recopilar la entrada y devolver PdfResponse.Maquetación de PDF compartida entre casos de uso.
Servicio de aplicaciónAplicaciónCargar los datos de dominio y elegir un builder.Lógica del compilador del contenedor de Symfony.
Servicio builderAplicaciónImplementar PdfBuilderInterface para construir documentos de forma síncrona o en cola.Objetos de solicitud, entity managers o contexto no serializable.
Bundle de Symfonynextpdf/symfonyRegistrar servicios, el árbol de configuración, el paso de extensión opcional, los helpers de respuesta y los DTO de Messenger.Política de almacenamiento específica de cada tenant.
Motor principalnextpdf/nextpdfCrear y serializar el documento.Comportamiento de la respuesta de Symfony o de Messenger.
EtapaComportamientoAcción del desarrollador
Arranque del bundleNextPdfBundle::build() registra la detección de extensiones opcionales.Dejar que Symfony descubra el bundle o registrarlo en bundles.php.
Carga de la configuraciónNextPdfExtension::load() procesa la configuración de nextpdf: y carga las definiciones de servicio.Mantener una configuración explícita y sensible al entorno.
Uso de la factoryPdfFactory::create() devuelve un documento nuevo ya configurado.No almacenar documentos en los servicios.
Salida del controladorPdfResponse convierte un documento terminado en una respuesta.Usar el helper en lugar de ensamblar las cabeceras manualmente.
Despacho de MessengerGeneratePdfMessage transporta la clase del builder, la ruta de salida y el contexto serializable.Mantener un contexto mínimo y apto para valores escalares.
Manejo del mensajeGeneratePdfHandler resuelve el builder desde un service locator y guarda el documento.Hacer que los builders sean deterministas e idempotentes.
RutaPropósito
src/Pdf/Builder/*Servicios que implementan PdfBuilderInterface.
src/Pdf/Data/*DTO pequeños o arrays utilizados como contexto del builder.
src/Pdf/Storage/*Selección del directorio raíz de almacenamiento y política de nombres de archivo de salida.
src/Controller/*Puntos de entrada de respuesta síncronos.
tests/Pdf/*Pruebas de builders, respuestas, Messenger y configuración.

Preferir los servicios builder en lugar de funciones helper estáticas. Son fáciles de etiquetar, decorar, probar y usar desde Messenger.

<?php
namespace App\Pdf\Builder;
use NextPDF\Core\Document;
use NextPDF\Symfony\Message\PdfBuilderInterface;
final readonly class InvoicePdfBuilder implements PdfBuilderInterface
{
public function build(Document $document, array $context): Document
{
$document->setTitle((string) $context['title'])
->addPage()
->writeHtml((string) $context['html']);
return $document;
}
}
<?php
namespace App\Controller;
use App\Pdf\Builder\InvoicePdfBuilder;
use NextPDF\Symfony\Http\PdfResponse;
use NextPDF\Symfony\Service\PdfFactory;
final readonly class InvoiceController
{
public function __invoke(
PdfFactory $factory,
InvoicePdfBuilder $builder,
) {
$document = $builder->build($factory->create(), [
'title' => 'Invoice 1234',
'html' => '<h1>Invoice 1234</h1>',
]);
return PdfResponse::download($document, 'invoice-1234.pdf');
}
}

Mantener reducido el contexto del controlador. Si un builder necesita muchos objetos de dominio, mover la orquestación a un servicio de aplicación y pasar al builder un DTO o un array normalizado.

GeneratePdfMessage valida la clase del builder y la ruta de salida antes del despacho. El handler vuelve a validar la ruta en tiempo de ejecución.

<?php
use App\Pdf\Builder\InvoicePdfBuilder;
use NextPDF\Symfony\Message\GeneratePdfMessage;
$bus->dispatch(new GeneratePdfMessage(
builderClass: InvoicePdfBuilder::class,
outputPath: $projectDir . '/var/pdfs/invoice-1234.pdf',
builderContext: [
'title' => 'Invoice 1234',
'html' => '<h1>Invoice 1234</h1>',
],
));

No colocar en builderContext entidades de Doctrine, streams abiertos, closures, objetos de solicitud ni objetos de servicio.

Punto de extensiónUso recomendadoRestricción
Decoración de servicio de PdfFactoryAplicar valores predeterminados de la aplicación antes de que los documentos lleguen a los controladores.Debe preservar la semántica de documento nuevo.
PdfBuilderInterfaceDefinir builders de documentos reutilizables o en cola.Debe devolver un Document.
OptionalExtensionPassHabilitar funcionalidades opcionales de Artisan o Premium en tiempo de compilación.La disponibilidad es el estado de compilación del contenedor, no el estado de la solicitud.
Árbol de configuración de SymfonyValores predeterminados, PDF/A, ajustes del renderer, firma, TSA, Messenger.Una configuración no válida debería fallar durante la compilación del contenedor.
Cableado de servicio de GeneratePdfHandlerRestringir qué builders son accesibles desde los mensajes en cola.El service locator solo debería exponer los servicios builder aprobados.
  1. Añadir un servicio builder con entrada determinista.
  2. Usar PdfFactory::create() en un controlador o servicio.
  3. Añadir una prueba de respuesta para el nombre de archivo, el tipo de contenido y las cabeceras.
  4. Registrar el builder para Messenger cuando el mismo documento deba generarse de forma asíncrona.
  5. Añadir pruebas de mensaje no válido para el nombre de clase, la ruta de salida y la forma del contexto.
  6. Añadir una prueba de compilación del contenedor con configuración mínima y de producción.
  7. Medir el tiempo de renderizado y la memoria con los mismos ajustes de PHP que en producción.
FalloDónde debería manejarseRespuesta recomendada
Configuración no válidaCompilación del contenedor.Hacer que el despliegue falle antes de que el tráfico llegue a la aplicación.
Servicio builder ausentePruebas del handler de Messenger y tags de servicio.Hacer que el mensaje falle y alertar al equipo responsable.
Ruta de salida inseguraConstructor del mensaje y política de almacenamiento.Rechazarla antes del despacho; mantener la validación del handler como defensa en profundidad.
Extensión opcional no disponibleCompiler pass y comportamiento de la factory.Desactivar la funcionalidad opcional o hacer explícita la instalación.
Fallo de conversión del servicio o de renderizadoLímite del builder.Fallar de forma cerrada salvo que el caso de uso tenga un fallback documentado.
AspectoPredeterminadoCuándo anularlo
Tiempo de vida de la factoryServicio del contenedor.Mantenerlo; la factory es segura porque crea documentos.
Tiempo de vida del documentoUna unidad de trabajo.Nunca compartirlo entre solicitudes o mensajes.
Validación de la ruta de salidaConstructor del mensaje y handler.Añadir restricciones de tenant o de directorio raíz de almacenamiento en el código de la aplicación.
Nombre de archivo de la respuestadocument.pdf.Anularlo con identificadores de negocio saneados.
Transporte de Messengerasync.Usar un transporte dedicado cuando el trabajo de PDF sea pesado.
  • Las pruebas del contenedor compilan el bundle con configuración mínima y de producción.
  • Las pruebas de respuesta verifican las cabeceras de seguridad y el manejo del nombre de archivo.
  • Las pruebas de Messenger verifican que las rutas no válidas y los nombres de clase de builder no válidos fallen antes del despacho.
  • Las pruebas del handler usan un servicio builder real y un directorio de salida temporal.
  • Las pruebas del builder renderizan un documento representativo y lo guardan con permisos del sistema de archivos similares a los de producción.
  • Las pruebas de extensión opcional cubren los casos de Artisan no disponible, Premium no disponible y el comportamiento del perfil PDF/A configurado.