Uso em produção do NextPDF Laravel
Visão geral
Seção intitulada “Visão geral”Em produção, resolva o contrato do documento por injeção no construtor. Trate falhas de escrita do PDF com uma exceção específica. Mova a geração pesada ou em lote para o GeneratePdfJob e conecte callbacks explícitos de sucesso e falha.
Instalação
Seção intitulada “Instalação”composer require nextpdf/laravelphp artisan vendor:publish --tag=nextpdf-configConfigure a conexão da fila em config/nextpdf.php. Defina queue.connection, queue.queue e queue.timeout. Em seguida, garanta que um worker esteja em execução na conexão configurada.
Visão conceitual
Seção intitulada “Visão conceitual”O container expõe NextPDF\Contracts\PdfDocumentInterface como um factory binding. Cada resolução produz um novo NextPDF\Core\Document. A PSR-11 permite que um container retorne valores diferentes em chamadas get() sucessivas, dependendo da estratégia de binding (PSR-11 §1.1.2). Este pacote usa um factory binding para que o estado mutável com escopo de requisição nunca atravesse de uma requisição para outra. Os registros de fontes e imagens são singletons. Isso preserva o contrato de que um identificador vinculado resolve para a entrada registrada (PSR-11 §1.1.2), enquanto compartilha recursos custosos ao longo da vida do worker.
Em código de produção, prefira a injeção pelo construtor à facade. Isso torna a dependência explícita e mantém o controller testável em unidade sem inicializar a raiz da facade.
Exemplo de código — Produção
Seção intitulada “Exemplo de código — Produção”Controller conectado por DI com tratamento de erros tipado
Seção intitulada “Controller conectado por DI com tratamento de erros tipado”<?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); } }}Injete PdfDocumentInterface, e não o Document concreto, para poder trocar o binding nos testes. O container retorna um novo documento a cada instanciação do controller. Não reutilize a mesma instância do controller para dois documentos não relacionados dentro de um único processo.
O bloco catch registra a classe da exceção e retorna um erro HTTP definido, em vez de vazar um stack trace. Use Psr\Log\LoggerInterface, que o container resolve para o logger do framework. A PSR-3 deixa o escape de placeholders a cargo de quem implementa e orienta os chamadores a não fazer pré-escape dos valores de contexto (PSR-3 §1.2). Passe contexto estruturado, não strings interpoladas.
Geração em fila com callbacks de sucesso e falha
Seção intitulada “Geração em fila com callbacks de sucesso e falha”GeneratePdfJob é um job ShouldQueue. Por padrão, ele usa três tentativas, um timeout de 120 segundos e um backoff de 10 segundos. Você pode sobrescrever os três em config/nextpdf.php. A closure do builder recebe o documento resolvido pelo container e deve retornar um 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() encaminha os argumentos diretamente ao construtor (string $outputPath, callable $builder, ?callable $onSuccess, ?callable $onFailure). Como resultado, os callbacks de sucesso e falha ficam conectados ao mesmo job que vai para a fila. Isso corresponde à forma posicional GeneratePdfJob::dispatch($path, $builder) em /integrations/laravel/quickstart/. O callback de sucesso recebe o caminho de saída, e o callback de falha recebe o Throwable. O job também expõe os setters fluentes then() e catch(), que retornam o job para encadeamento. Use esses setters apenas quando você mantém e despacha essa mesma instância, por exemplo, por meio do helper dispatch(). O job também expõe um método failed(), que o queue runner invoca em caso de falha terminal. Os callbacks são empacotados em closures serializáveis para sobreviverem ao transporte da fila.
Ajuste do comportamento da fila
Seção intitulada “Ajuste do comportamento da fila”| Propriedade | Padrão | Chave de config |
|---|---|---|
tries | 3 | não vem da config; crie uma subclasse para alterar |
timeout | 120 | nextpdf.queue.timeout |
backoff | 10 | não vem da config; crie uma subclasse para alterar |
| nome da fila | pdf | nextpdf.queue.queue |
| conexão | default | nextpdf.queue.connection |
tries e backoff são propriedades públicas lidas da instância do job. O job incluído no pacote não as lê da config. Se a política de novas tentativas for diferente, crie uma subclasse de GeneratePdfJob para sobrescrevê-las.
Casos extremos e pegadinhas
Seção intitulada “Casos extremos e pegadinhas”- A closure do builder deve retornar um
PdfDocumentInterface. O job salva esse valor de retorno, não a instância resolvida originalmente. O teste do job verifica esse contrato explicitamente. - Resolver
SignerInterfaceretornanull, a menos que a assinatura esteja habilitada, um certificado esteja configurado e onextpdf/premiumesteja instalado. Sempre verifique se é null antes de assinar. - Workers de longa duração (Octane/RoadRunner/Swoole) compartilham o registro de fontes bloqueado. Configure
preload_fontspara que o aquecimento aconteça uma vez na inicialização do worker, em vez de ocorrer na primeira requisição. - Um job que falhou invoca
failed()após esgotartries. A falha por tentativa não chamaonFailureaté que o queue runner declare a falha terminal.
Desempenho
Seção intitulada “Desempenho”A geração síncrona no controller bloqueia a requisição durante toda a construção do PDF. Para saídas de várias páginas ou em lote, despache o GeneratePdfJob e retorne imediatamente. Os registros singleton distribuem o parsing de fontes e a decodificação de imagens ao longo da vida do worker. Assim, o custo por requisição fica limitado à construção do documento e à emissão do conteúdo.
Notas de segurança
Seção intitulada “Notas de segurança”O controller com injeção de dependências registra a classe da exceção, não a mensagem nem o trace, para evitar o vazamento de detalhes internos nos logs. O GeneratePdfJob valida o caminho de saída no worker para mitigar payloads serializados adulterados no transporte da fila. A cobertura completa está em /integrations/laravel/security-and-operations/.
Conformidade
Seção intitulada “Conformidade”| Afirmação | Fonte | Cláusula | reference_id |
|---|---|---|---|
| Identificador vinculado resolve para sua entrada registrada | PSR-11 Container | §1.1.2 | |
| Resoluções sucessivas podem diferir conforme a estratégia de binding (factory binding) | PSR-11 Container | §1.1.2 |
As orientações de logging da PSR-3 aparecem na especificação PSR-3. Elas atribuem o escape de placeholders a quem implementa e orientam os chamadores a passar contexto estruturado. Consulte o doc psr_3_logger §1.2.
Contexto comercial
Seção intitulada “Contexto comercial”A saída assinada PAdES B-B e o arquivamento PDF/A via nextpdf/premium usam a mesma superfície de injeção de dependências (DI). Este é um recurso Enterprise opcional. O pacote Core documentado aqui não exige nenhuma alteração de código para adotá-lo. Consulte https://nextpdf.dev/get-license/?intent=laravel-signing.
Veja também
Seção intitulada “Veja também”- /integrations/laravel/quickstart/ — primeiro exemplo mínimo
- /integrations/laravel/configuration/ — chaves de fila, assinatura e fonte
- /integrations/laravel/security-and-operations/ — modelo de ameaças e hardening
- /integrations/laravel/troubleshooting/ — falhas comuns em produção