跳到內容

Symfony 開發者指南

Symfony 套件採 service 優先設計。注入 PdfFactory,對每份文件呼叫 create(),並用 Messenger builder 處理非同步產生。由於每次呼叫都會回傳一份全新文件,這個 factory 可以註冊為 container service。

當你設計 controller、service、Messenger handler,或圍繞 nextpdf/symfony 的 bundle 層擴充點時,請參考本指南。

層級負責方職責不要放這裡
Controller(控制器)應用程式對請求進行授權、收集輸入,並回傳 PdfResponse跨多種使用情境共用的 PDF 版面。
應用程式 service應用程式載入領域資料,並選定一個 builder。Symfony container 編譯邏輯。
Builder service(建構器服務)應用程式實作 PdfBuilderInterface,供同步或排入佇列的文件建構使用。請求物件、entity manager,或無法序列化的 context。
Symfony bundle(套件)nextpdf/symfony註冊 service、config tree、選用的 extension pass、回應輔助工具,以及 Messenger DTO。租戶專屬的儲存策略。
核心引擎nextpdf/nextpdf撰寫並序列化文件。Symfony 回應或 Messenger 行為。
階段行為開發者動作
Bundle 啟動NextPdfBundle::build() 會註冊選用擴充功能的偵測。讓 Symfony 自動探索這個 bundle,或在 bundles.php 中手動註冊。
載入設定NextPdfExtension::load() 會處理 nextpdf: 設定並載入 service 定義。讓設定維持明確,並能感知環境。
使用 factoryPdfFactory::create() 會回傳一份全新且已設定好的文件。不要把文件存放在 service 裡。
Controller 輸出PdfResponse 會把完成的文件轉成回應。使用這個輔助工具,不要手動組裝標頭。
Messenger 派發GeneratePdfMessage 會攜帶 builder 類別、輸出路徑,以及可序列化的 context。讓 context 保持精簡,並以純量為主。
處理訊息GeneratePdfHandler 會透過 service locator resolve(解析)並取得 builder,再儲存文件。讓 builder 維持決定性與冪等性。
路徑用途
src/Pdf/Builder/*實作 PdfBuilderInterface 的 service。
src/Pdf/Data/*作為 builder context 使用的小型 DTO 或陣列。
src/Pdf/Storage/*儲存根目錄的選擇與輸出檔名策略。
src/Controller/*同步回應的進入點。
tests/Pdf/*builder、回應、Messenger 與設定的測試。

優先採用 builder service,而非靜態輔助函式。這些 service 易於標記、裝飾、測試,也方便從 Messenger 使用。

<?php
namespace App\Pdf\Builder;
use NextPDF\Core\Document;
use NextPDF\Symfony\Message\PdfBuilderInterface;
final readonly class InvoicePdfBuilder implements PdfBuilderInterface
{
public function build(Document $document, array $context): Document
{
$document->setTitle((string) $context['title'])
->addPage()
->writeHtml((string) $context['html']);
return $document;
}
}
<?php
namespace App\Controller;
use App\Pdf\Builder\InvoicePdfBuilder;
use NextPDF\Symfony\Http\PdfResponse;
use NextPDF\Symfony\Service\PdfFactory;
final readonly class InvoiceController
{
public function __invoke(
PdfFactory $factory,
InvoicePdfBuilder $builder,
) {
$document = $builder->build($factory->create(), [
'title' => 'Invoice 1234',
'html' => '<h1>Invoice 1234</h1>',
]);
return PdfResponse::download($document, 'invoice-1234.pdf');
}
}

讓 controller 的 context 保持精簡。如果某個 builder 需要許多領域物件,就把這段協調邏輯移到應用程式 service,再把 DTO 或正規化後的陣列傳給 builder。

GeneratePdfMessage 會在派發前驗證 builder 類別與輸出路徑。 handler 會在執行時再次驗證該路徑。

<?php
use App\Pdf\Builder\InvoicePdfBuilder;
use NextPDF\Symfony\Message\GeneratePdfMessage;
$bus->dispatch(new GeneratePdfMessage(
builderClass: InvoicePdfBuilder::class,
outputPath: $projectDir . '/var/pdfs/invoice-1234.pdf',
builderContext: [
'title' => 'Invoice 1234',
'html' => '<h1>Invoice 1234</h1>',
],
));

不要把 Doctrine entity、開啟中的串流、closure、請求物件或 service 物件放進 builderContext

擴充點適用情境限制
PdfFactory service 裝飾在文件交給 controller 之前,套用應用程式層級的預設值。必須保留全新文件的語意。
PdfBuilderInterface定義可排入佇列或可重複使用的文件 builder。必須回傳一個 Document
OptionalExtensionPass在編譯期啟用選用的 Artisan 或 Premium 功能。可用與否取決於 container 編譯狀態,而非請求狀態。
Symfony config tree(組態樹)預設值、PDF/A、renderer 設定、簽章、TSA、Messenger。無效的設定應在 container 建置時就失敗。
GeneratePdfHandler service 接線限制排入佇列的訊息可以存取哪些 builder。service locator 應只暴露經核准的 builder service。
  1. 新增一個對輸入具決定性的 builder service。
  2. 在 controller 或 service 中使用 PdfFactory::create()
  3. 針對檔名、內容類型與標頭新增回應測試。
  4. 當同一份文件必須以非同步方式產生時,為 Messenger 註冊該 builder。
  5. 針對類別名稱、輸出路徑與 context 結構,新增無效訊息的測試。
  6. 以最小設定與正式環境設定,新增一個 container 編譯測試。
  7. 在與正式環境相同的 PHP 設定下,量測 render 時間與記憶體用量。
失敗應在何處處理建議的回應
無效的設定Container 編譯。在流量抵達應用程式之前讓部署失敗。
缺少 builder serviceMessenger handler 測試與 service tag。讓訊息失敗,並通知負責的團隊。
不安全的輸出路徑訊息建構子與儲存策略。在派發前就拒絕;handler 的驗證則保留作為縱深防禦。
選用擴充功能無法使用compiler pass 與 factory 行為。停用選用功能,或明確要求安裝。
service 轉換或 render 失敗builder 邊界。除非該使用情境有書面記載的後援機制,否則一律 fail closed。
關注點預設值何時覆寫
Factory 生命週期Container service(容器服務)。保留這項設定;factory 之所以安全,是因為它只負責建立文件。
文件生命週期一個工作單元。絕不要跨請求或跨訊息共用。
輸出路徑驗證訊息建構子與 handler。在應用程式碼中加入租戶或儲存根目錄的限制。
回應檔名document.pdf以淨化過的業務識別碼覆寫。
Messenger transport(Messenger 傳輸)async當 PDF 工作負載很重時,使用專屬的 transport。
  • container 測試會以最小設定與正式環境設定編譯這個 bundle。
  • 回應測試會驗證安全標頭與檔名處理。
  • Messenger 測試會驗證無效路徑與無效的 builder 類別名稱在派發前就失敗。
  • handler 測試會使用真實的 builder service 與暫存輸出目錄。
  • builder 測試會 render 一份具代表性的文件,並在類似正式環境的檔案系統權限下儲存它。
  • 選用擴充功能的測試涵蓋 Artisan 不可用、Premium 不可用,以及已設定的 PDF/A profile 行為。