Uso in produzione del pacchetto NextPDF per Laravel
In breve
Sezione intitolata “In breve”In produzione, risolvere il contratto del documento tramite constructor injection. Gestire gli errori di scrittura del PDF con un’eccezione specifica. Delegare la generazione pesante o in batch a GeneratePdfJob e associare callback esplicite di successo e di errore.
Installazione
Sezione intitolata “Installazione”composer require nextpdf/laravelphp artisan vendor:publish --tag=nextpdf-configConfigurare la connessione alla coda in config/nextpdf.php. Impostare queue.connection, queue.queue e queue.timeout. Verificare quindi che sia in esecuzione un worker per la connessione configurata.
Panoramica concettuale
Sezione intitolata “Panoramica concettuale”Il container espone NextPDF\Contracts\PdfDocumentInterface come factory binding. Ogni risoluzione produce un nuovo NextPDF\Core\Document. PSR-11 consente a un container di restituire valori diversi da chiamate get() successive, a seconda della strategia di binding (PSR-11 §1.1.2). Qui il pacchetto utilizza un factory binding affinché lo stato mutabile con ambito di richiesta non passi mai da una richiesta all’altra. I registry di font e immagini sono singleton. In questo modo si mantiene il contratto secondo cui un identificatore associato si risolve nella voce registrata (PSR-11 §1.1.2), pur condividendo le risorse costose per l’intero worker.
Nel codice di produzione, preferire la constructor injection alla facade. In questo modo la dipendenza diventa esplicita e il controller resta testabile a livello unitario senza avviare la facade root.
Esempio di codice — Produzione
Sezione intitolata “Esempio di codice — Produzione”Controller collegato tramite DI con gestione tipizzata degli errori
Sezione intitolata “Controller collegato tramite DI con gestione tipizzata degli errori”<?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); } }}Iniettare PdfDocumentInterface, non il Document concreto, così che il binding rimanga sostituibile nei test. Il container restituisce un nuovo documento per ogni istanziazione del controller. Non riutilizzare la stessa istanza del controller per due documenti non correlati all’interno dello stesso processo.
Il blocco catch registra nel log la classe dell’eccezione e restituisce un errore HTTP definito, anziché esporre uno stack trace. Usare Psr\Log\LoggerInterface, che il container risolve nel logger del framework. PSR-3 lascia l’escaping dei segnaposto all’implementatore e indica ai chiamanti di non applicare escaping preventivo ai valori di contesto (PSR-3 §1.2). Passare un contesto strutturato, non stringhe interpolate.
Generazione in coda con callback di successo e di errore
Sezione intitolata “Generazione in coda con callback di successo e di errore”GeneratePdfJob è un job ShouldQueue. Per impostazione predefinita prevede tre tentativi, un timeout di 120 secondi e un backoff di 10 secondi. È possibile sovrascrivere tutti e tre i valori tramite config/nextpdf.php. La closure builder riceve il documento risolto dal container e deve restituire il documento configurato.
<?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() inoltra i propri argomenti direttamente al costruttore (string $outputPath, callable $builder, ?callable $onSuccess, ?callable $onFailure). In questo modo le callback di successo e di errore vengono collegate allo stesso job messo in coda. È la stessa forma posizionale di GeneratePdfJob::dispatch($path, $builder) in /integrations/laravel/quickstart/. La callback di successo riceve il percorso di output, mentre la callback di errore riceve il Throwable. Il job espone inoltre setter fluent then() e catch() che restituiscono il job per consentire il concatenamento. Usare tali setter solo quando si conserva e si invia in coda quella stessa istanza, ad esempio tramite l’helper dispatch(). Il job espone anche un metodo failed(), che il queue runner richiama in caso di errore terminale. Le callback sono incapsulate in closure serializzabili in modo da sopravvivere al trasporto della coda.
Ottimizzazione del comportamento della coda
Sezione intitolata “Ottimizzazione del comportamento della coda”| Proprietà | Valore predefinito | Chiave di configurazione |
|---|---|---|
tries | 3 | non guidato dalla configurazione; creare una sottoclasse per modificarlo |
timeout | 120 | nextpdf.queue.timeout |
backoff | 10 | non guidato dalla configurazione; creare una sottoclasse per modificarlo |
| nome della coda | pdf | nextpdf.queue.queue |
| connessione | default | nextpdf.queue.connection |
tries e backoff sono proprietà pubbliche lette dall’istanza del job. Il job fornito non ne deriva i valori dalla configurazione. Per sovrascriverli quando la policy di retry deve differire, creare una sottoclasse di GeneratePdfJob.
Casi limite e insidie
Sezione intitolata “Casi limite e insidie”- La closure builder deve restituire un
PdfDocumentInterface. Il job salva quel valore di ritorno, non l’istanza risolta in origine. Il test del job verifica esplicitamente questo contratto. - La risoluzione di
SignerInterfacerestituiscenulla meno che la firma non sia abilitata, un certificato non sia configurato enextpdf/premiumnon sia installato. Verificare sempre la presenza di null prima di firmare. - I worker di lunga durata (Octane/RoadRunner/Swoole) condividono il registry dei font bloccato. Configurare
preload_fontsaffinché il warmup avvenga una sola volta all’avvio del worker, anziché alla prima richiesta. - Un job fallito richiama
failed()dopo aver esaurito itries. L’errore del singolo tentativo non richiamaonFailurefinché il queue runner non dichiara un errore terminale.
Prestazioni
Sezione intitolata “Prestazioni”La generazione sincrona nel controller blocca la richiesta per l’intera creazione del PDF. Per output su più pagine o in batch, inviare GeneratePdfJob alla coda e restituire immediatamente il controllo. I registry singleton ammortizzano il parsing dei font e la decodifica delle immagini lungo l’intero ciclo di vita del worker. Il costo per richiesta è quindi limitato alla costruzione del documento e all’emissione del contenuto.
Note sulla sicurezza
Sezione intitolata “Note sulla sicurezza”Il controller con DI registra nel log la classe dell’eccezione, non il relativo messaggio o trace, per evitare di esporre dettagli interni nei log. GeneratePdfJob convalida il percorso di output sul worker per mitigare payload serializzati manomessi durante il trasporto della coda. La trattazione completa è in /integrations/laravel/security-and-operations/.
Conformità
Sezione intitolata “Conformità”| Dichiarazione | Fonte | Clausola | reference_id |
|---|---|---|---|
| Un identificatore associato si risolve nella voce registrata | PSR-11 Container | §1.1.2 | |
| Le risoluzioni successive possono variare in base alla strategia di binding (factory binding) | PSR-11 Container | §1.1.2 |
Le linee guida di logging di PSR-3 sono documentate nella specifica PSR-3. Queste linee guida stabiliscono che l’escaping dei segnaposto è responsabilità dell’implementatore e che i chiamanti passano un contesto strutturato. Vedere il documento psr_3_logger §1.2.
Contesto commerciale
Sezione intitolata “Contesto commerciale”L’output firmato PAdES B-B e l’archiviazione PDF/A tramite nextpdf/premium utilizzano la stessa superficie di DI. Si tratta di una funzionalità Enterprise opzionale. Il pacchetto Core qui documentato non richiede alcuna modifica al codice per adottarla. Vedere https://nextpdf.dev/get-license/?intent=laravel-signing.
Vedere anche
Sezione intitolata “Vedere anche”- /integrations/laravel/quickstart/ — primo esempio minimo
- /integrations/laravel/configuration/ — chiavi di coda, firma e font
- /integrations/laravel/security-and-operations/ — modello delle minacce e hardening
- /integrations/laravel/troubleshooting/ — errori comuni in produzione