跳到內容

Laravel 開發者指南

Laravel 套件讓 NextPDF 順應 Laravel 慣例,而不改動核心文件生命週期。容器(container)擁有共用的登錄表(registry)與工廠(factory)。每份 PDF 文件都屬於用完即拋,只應被建構、回傳、串流或儲存一次。

當你設計以 nextpdf/laravel 為核心的應用程式服務、佇列工作(queue job)、回應流程或測試覆蓋率時,請使用本指南。

層級擁有者職責不要放在這裡
控制器(controller)應用程式對請求授權、選擇文件建構器(builder),並回傳回應。跨使用情境共用的 PDF 版面規則。
應用程式服務應用程式收集領域資料,並呼叫用來建構文件的程式碼。容器啟動邏輯或套件組態。
文件建構器(builder)應用程式將領域資料轉換成 NextPDF 文件呼叫。請求物件、Eloquent 查詢邏輯,或佇列傳輸(transport)細節。
Laravel 整合nextpdf/laravel綁定工廠、登錄表、簽章器(signer)、TSA 用戶端、facade、回應與佇列工作。業務特定的儲存路徑或租戶政策。
核心引擎nextpdf/nextpdf建構並序列化 PDF。Laravel 回應、佇列或檔案系統政策。
階段行為開發者動作
服務提供者註冊NextPdfServiceProvider::register() 會註冊共用登錄表、文件工廠、文件綁定、HTTP 用戶端、TSA 用戶端、簽章器,以及選用的電子發票契約。在進入正式環境前,請先發佈並檢視 config/nextpdf.php
resolve(解析)文件Pdf facade 與 PdfDocumentInterface 綁定會透過 DocumentFactoryInterface 解析出一份全新文件。每個請求、命令或佇列工作只解析一次文件。
撰寫應用程式程式碼會透過 facade 或注入的文件,呼叫核心文件 API。請將領域資料擷取留在文件建構器之外。
終端輸出PdfResponse 會輸出 HTTP 內容,或將文件儲存到磁碟。每份文件只選擇一條終端輸出路徑。
佇列執行GeneratePdfJob 會在 worker 內重新建構文件,並再次驗證輸出路徑。傳入純量情境資料,並讓回呼(callback)保持冪等。
路徑用途
app/Pdf/Builders/*純文件建構器。它們接收資料並回傳一份已完成的文件。
app/Pdf/Data/*承載已授權文件輸入的小型 DTO。
app/Services/*應用程式編排、查詢、授權交接,以及儲存路徑選擇。
app/Jobs/*當應用程式需要具名工作時,用來包裝 GeneratePdfJob 的選用包裝器。
tests/Feature/Pdf/*HTTP 回應、佇列派送與授權測試。
tests/Unit/Pdf/*以小型、具決定性輸入進行的建構器測試。

讓建構器與 Laravel 請求物件保持獨立。建構器應該能以相同輸入,由控制器、命令、測試與佇列 worker 呼叫。

<?php
namespace App\Pdf\Builders;
use App\Pdf\Data\InvoicePdfData;
use NextPDF\Contracts\PdfDocumentInterface;
final readonly class InvoicePdfBuilder
{
public function build(PdfDocumentInterface $pdf, InvoicePdfData $data): PdfDocumentInterface
{
$pdf->setTitle($data->title)
->addPage()
->setFont('dejavusans', '', 12)
->writeHtml($data->html);
return $pdf;
}
}

當 PDF 流程屬於應用程式邏輯的一部分時,請使用建構函式注入。只有在短小的控制器流程中,且靜態寫法能提升可讀性時,才使用 facade。

<?php
namespace App\Http\Controllers;
use App\Pdf\Builders\InvoicePdfBuilder;
use App\Pdf\Data\InvoicePdfData;
use NextPDF\Contracts\PdfDocumentInterface;
use NextPDF\Laravel\Http\PdfResponse;
final readonly class DownloadInvoiceController
{
public function __invoke(
PdfDocumentInterface $pdf,
InvoicePdfBuilder $builder,
) {
$document = $builder->build(
$pdf,
InvoicePdfData::fromInvoiceId(1234),
);
return PdfResponse::download($document, 'invoice-1234.pdf');
}
}

回應輔助程式會在建構 Laravel 回應之前,先將文件位元組具現化。它們是回應輔助程式,不是背景 renderer(渲染器)。

GeneratePdfJob 接受一個建構器可呼叫項目與輸出路徑。這個工作會在執行期驗證不安全的路徑。應用程式程式碼仍應在派送前,先選定一個租戶安全的儲存根目錄。

<?php
use App\Pdf\Builders\QueuedInvoiceBuilder;
use NextPDF\Laravel\Jobs\GeneratePdfJob;
GeneratePdfJob::dispatch(
outputPath: storage_path('app/pdfs/invoice-1234.pdf'),
builder: [QueuedInvoiceBuilder::class, 'build'],
)->onQueue(config('nextpdf.queue.queue', 'pdf'));

佇列回呼應該保持精簡。比起把複雜的閉包(closure)存進佇列酬載,建議改由應用程式的工作監聽器寫入持久狀態。

擴充點用途限制
PdfDocumentInterface 綁定替換或裝飾文件建立流程,以套用整個應用程式的預設值。必須回傳一個全新的文件實例。
DocumentFactoryInterface在服務與測試中明確建立全新文件。不要快取回傳的文件。
config/nextpdf.php預設值、佇列設定、Chrome renderer(渲染器)設定、簽署掛鉤、TSA、OCSP 快取。請把環境變數當成部署組態,而不是請求輸入。
GeneratePdfJob 建構器以非同步方式建構文件。可呼叫項目必須能由 Laravel 的佇列傳輸序列化。
成功/失敗回呼產生後的通知或清理。讓回呼保持冪等並留意副作用。
選用的 Premium 契約電子發票嵌入器、驗證器、設定檔與 Schematron 執行器。只在選用套件已安裝且已授權的地方解析。
  1. 先在控制器或功能測試中同步建構第一份文件。
  2. 把版面程式碼搬進 app/Pdf/Builders 底下的建構器類別。
  3. 把查詢與授權邏輯搬進應用程式服務。
  4. PdfResponse 加入標頭與檔名的測試。
  5. 把緩慢或大量的產生作業移到 GeneratePdfJob
  6. 為序列化情境、輸出路徑政策與失敗處理加上佇列測試。
  7. 使用具代表性的正式環境資料測量記憶體與 render 時間。
失敗應該在哪裡處理建議的回應
無效的請求或未授權的文件控制器或政策。回傳應用程式慣用的授權或驗證回應。
缺少字型或無效影像建構器測試與應用程式記錄。讓請求或工作失敗;不要輸出不完整的 PDF。
不安全的輸出路徑應用程式儲存服務與 GeneratePdfJob在派送前就拒絕,並以 worker 端驗證作為縱深防禦。
簽署或 TSA 失敗簽署服務邊界。決定文件是否允許未簽署;對受監管的文件,預設採用 fail closed(失敗即拒)。
佇列逾時佇列 worker 組態與可觀測性。只有在建構器具決定性、且輸出路徑可安全覆寫時,才重試。
關注點預設值何時覆寫
佇列名稱pdf當產生作業會與面向使用者的工作競爭資源時,請使用專用佇列。
工作逾時120只有在測量過文件大小與 worker 容量後,才調高。
回應檔名document.pdf使用經過淨化的商業識別碼。
字型登錄表共用,並在暖機後鎖定。為熱路徑上使用的字型加入 preload_fonts
影像登錄表共用的有界快取。在記憶體受限的 worker 上調低 image_cache_mb
串流回應分塊64 KB 的區塊。不要依賴區塊邊界;那只是輸出細節。
  • 控制器測試要斷言 Content-TypeContent-Disposition 與防禦性標頭。
  • 建構器測試使用具決定性的 DTO,且不查詢資料庫。
  • 佇列測試要斷言建構器收到的是一份全新文件。
  • 路徑測試涵蓋路徑遍歷、stream-wrapper、null 位元組,以及非 .pdf 的拒絕。
  • worker 測試要在與正式環境相同的記憶體上限下,render 具代表性的文件。
  • 選用的簽署測試要涵蓋缺少憑證、密碼錯誤、TSA 無法使用,以及已設定的簽章層級。