Pular para o conteúdo

Uso em produção do NextPDF Laravel

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.

Terminal window
composer require nextpdf/laravel
php artisan vendor:publish --tag=nextpdf-config

Configure 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.

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.

Controller conectado por DI com tratamento de erros tipado

Seção intitulada “Controller conectado por DI com tratamento de erros tipado”
resource: NextPDF\Contracts\PdfDocumentInterface + src/Laravel/Http/PdfResponse.php
<?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.

resource: src/Laravel/Jobs/GeneratePdfJob.php
<?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.

PropriedadePadrãoChave de config
tries3não vem da config; crie uma subclasse para alterar
timeout120nextpdf.queue.timeout
backoff10não vem da config; crie uma subclasse para alterar
nome da filapdfnextpdf.queue.queue
conexãodefaultnextpdf.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.

  • 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 SignerInterface retorna null, a menos que a assinatura esteja habilitada, um certificado esteja configurado e o nextpdf/premium esteja instalado. Sempre verifique se é null antes de assinar.
  • Workers de longa duração (Octane/RoadRunner/Swoole) compartilham o registro de fontes bloqueado. Configure preload_fonts para 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 esgotar tries. A falha por tentativa não chama onFailure até que o queue runner declare a falha terminal.

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.

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/.

AfirmaçãoFonteCláusulareference_id
Identificador vinculado resolve para sua entrada registradaPSR-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.

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.

  • /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