NextPDF Laravel 套件正式環境用法
在正式環境中,請透過建構式注入 resolve(解析)文件契約。針對 PDF 寫入失敗,請處理特定例外。將繁重或批次產生作業移至 GeneratePdfJob,並明確接上成功與失敗回呼。
composer require nextpdf/laravelphp artisan vendor:publish --tag=nextpdf-config請在 config/nextpdf.php 中設定佇列連線。設定 queue.connection、queue.queue 與 queue.timeout。接著確認已有 worker 正在處理已設定的連線。
概念總覽
標題為「概念總覽」的區段容器將 NextPDF\Contracts\PdfDocumentInterface 公開為工廠(factory)繫結。每次解析都會產生一個全新的 NextPDF\Core\Document。PSR-11 允許容器依繫結策略,在連續的 get() 呼叫中回傳不同的值(PSR-11 §1.1.2)。本套件在這裡採用工廠繫結,讓以請求為作用範圍的可變狀態永遠不會跨請求外洩。字型與影像的登錄表則是單例(singleton)。如此既維持「繫結的識別碼會解析到其註冊項目」這項契約(PSR-11 §1.1.2),又能在整個 worker 生命週期中共用這些昂貴資源。
在正式環境程式碼中,請優先採用建構式注入,而非 facade。這讓相依關係更明確,也讓控制器在單元測試時無須啟動 facade 根節點。
程式碼範例——正式環境
標題為「程式碼範例——正式環境」的區段以 DI 接線且具型別錯誤處理的控制器
標題為「以 DI 接線且具型別錯誤處理的控制器」的區段<?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); } }}請注入 PdfDocumentInterface,而非具體的 Document,讓繫結在測試中仍可替換。容器每次具現化控制器時,都會回傳一份全新的文件。不要在同一個行程內,使用同一個控制器實例處理兩份不相關的文件。
catch 區塊會記錄例外類別,並回傳已定義的 HTTP 錯誤,而不會洩漏堆疊追蹤。請使用 Psr\Log\LoggerInterface;容器會將它解析為 Framework(框架)的記錄器。PSR-3 將 placeholder(佔位符)的跳脫交由實作者處理,並要求呼叫端不要預先跳脫情境值(PSR-3 §1.2)。請傳入結構化的情境資料,而非內插過的字串。
包含成功與失敗回呼的佇列產生流程
標題為「包含成功與失敗回呼的佇列產生流程」的區段GeneratePdfJob 是一個 ShouldQueue 工作。它預設為三次嘗試、120 秒逾時,以及 10 秒退避。你可以透過 config/nextpdf.php 覆寫這三者。建構器(builder)閉包會收到由容器解析的文件,並且必須回傳一份設定完成的文件。
<?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() 會把它的引數直接轉送給建構子 (string $outputPath, callable $builder, ?callable $onSuccess, ?callable $onFailure)。因此成功與失敗回呼會接到實際入列的那個工作上。這和 /integrations/laravel/quickstart/. 中 GeneratePdfJob::dispatch($path, $builder) 的位置引數形式一致。成功回呼會收到輸出路徑,失敗回呼則會收到 Throwable。這個工作也提供流暢式的 then() 與 catch() 設定方法,回傳工作本身以便串接。只有在你保留並派送同一個實例時,才使用這些設定方法,例如透過 dispatch() 輔助函式。這個工作也提供 failed() 方法,佇列執行器會在最終失敗時呼叫它。回呼會封裝成可序列化的閉包,因此能在佇列傳輸中保留下來。
調校佇列行為
標題為「調校佇列行為」的區段| 屬性 | 預設值 | 設定鍵 |
|---|---|---|
tries | 3 | 非由組態驅動;以子類別變更 |
timeout | 120 | nextpdf.queue.timeout |
backoff | 10 | 非由組態驅動;以子類別變更 |
| 佇列名稱 | pdf | nextpdf.queue.queue |
| 連線 | 預設 | nextpdf.queue.connection |
tries 與 backoff 是從工作實例讀取的公開屬性。隨附的工作並不是透過組態驅動這兩者。若你的重試策略不同,需要覆寫它們,請建立 GeneratePdfJob 的子類別。
邊界情況與陷阱
標題為「邊界情況與陷阱」的區段- 建構器閉包必須回傳一個
PdfDocumentInterface。工作儲存的是這個回傳值,而非一開始解析出的實例。工作測試會明確斷言這項契約。 - 解析
SignerInterface會回傳null,除非已啟用簽章、已設定憑證,且已安裝nextpdf/premium。簽章前務必先做 null 檢查。 - 長生命週期的 worker(Octane/RoadRunner/Swoole)會共用已鎖定的字型登錄表。請設定
preload_fonts,讓暖機在 worker 開機時發生一次,而不是落到第一個請求才進行。 - 工作會在用盡
tries、失敗確定後呼叫failed()。在佇列執行器宣告最終失敗之前,單次嘗試失敗並不會呼叫onFailure。
在控制器中同步產生 PDF,會在整個建構期間阻塞請求。對於多頁或批次輸出,請派送 GeneratePdfJob 並立即回傳。單例登錄表會把字型剖析與影像解碼分攤到整個 worker 生命週期。如此一來,每個請求的成本就只限於文件建構與內容輸出。
安全性注意事項
標題為「安全性注意事項」的區段DI 控制器只記錄例外類別,不記錄其訊息或追蹤資訊,以免將內部細節洩漏到日誌中。GeneratePdfJob 會在 worker 端驗證輸出路徑,以降低佇列傳輸中遭竄改的序列化承載所帶來的風險。完整內容請見 /integrations/laravel/security-and-operations/.
符合性
標題為「符合性」的區段| 主張 | 來源 | 條款 | 參考 ID |
|---|---|---|---|
| 繫結的識別碼會解析到其註冊項目 | PSR-11 Container(容器) | §1.1.2 | |
| 連續解析的結果可能因繫結策略而不同(工廠繫結) | PSR-11 Container(容器) | §1.1.2 |
PSR-3 的日誌指引載於 PSR-3 規範。該指引說明,佔位符跳脫由實作者負責,且呼叫端應傳入結構化的情境資料。請見文件 psr_3_logger §1.2。
商業情境
標題為「商業情境」的區段透過 nextpdf/premium 產出的已簽署 PAdES B-B 輸出與 PDF/A 封存,使用的是同一套 DI 介面。這是一項選用的 Enterprise 能力。這裡所記載的 Core 套件,不需任何程式碼變更即可採用它。請見 https://nextpdf.dev/get-license/?intent=laravel-signing。
另請參閱
標題為「另請參閱」的區段- /integrations/laravel/quickstart/ —— 最精簡的第一個範例
- /integrations/laravel/configuration/ —— 佇列、簽章與字型相關的設定鍵
- /integrations/laravel/security-and-operations/ —— 威脅模型與強化
- /integrations/laravel/troubleshooting/ —— 常見的正式環境故障