NextPDF Symfony 快速上手
注入 PdfFactory、建立一份 Document,再透過 PdfResponse 回傳。若要在背景產生 PDF,請將一則 GeneratePdfMessage 分派到 Messenger 傳輸層。
步驟 1 — 在控制器中產生 PDF
標題為「步驟 1 — 在控制器中產生 PDF」的區段注入 NextPDF\Symfony\Service\PdfFactory。它的 create() 方法會回傳一份全新的 NextPDF\Core\Document。系統會自動套用已設定的預設值:建立者、作者與語言。透過 NextPDF\Symfony\Http\PdfResponse 回傳這份文件。
<?php
declare(strict_types=1);
namespace App\Controller;
use NextPDF\Symfony\Http\PdfResponse;use NextPDF\Symfony\Service\PdfFactory;use Symfony\Component\HttpFoundation\Response;use Symfony\Component\Routing\Attribute\Route;
final class InvoiceController{ #[Route('/invoice/{number}', name: 'invoice_pdf')] public function download(PdfFactory $pdf, string $number): Response { $doc = $pdf->create(); $doc->addPage(); $doc->cell(0, 10, "Invoice #{$number}", newLine: true); $doc->cell(0, 10, 'Thank you for your business.');
return PdfResponse::download($doc, "invoice-{$number}.pdf"); }}PdfResponse::download() 會回傳一個 Symfony\Component\HttpFoundation\Response。它會包含 Content-Type: application/pdf、attachment 處置方式、一個 Content-Length,以及 bundle 固定套用的安全標頭。Symfony 官方文件記載了標準的 Response 類別及其標頭模型(https://symfony.com/doc/current/components/http_foundation.html)。
步驟 2 — 以內嵌方式顯示 PDF
標題為「步驟 2 — 以內嵌方式顯示 PDF」的區段若要讓瀏覽器直接顯示 PDF,而不是下載它,請使用 inline():
return PdfResponse::inline($doc, 'preview.pdf');處置方式會改為 inline。其餘標頭維持不變。
步驟 3 — 串流傳輸大型 PDF
標題為「步驟 3 — 串流傳輸大型 PDF」的區段對於大型文件,串流版本會以 64 KB 為單位分塊輸出 PDF,可降低尖峰記憶體用量。這類方法會回傳一個 Symfony\Component\HttpFoundation\StreamedResponse,並省略 Content-Length。
return PdfResponse::streamDownload($doc, 'annual-report.pdf');streamInline() 則是內嵌顯示的對應版本。Symfony 官方文件記載了 StreamedResponse 的回呼契約:它是一個負責 flush 輸出的 void callable(https://symfony.com/doc/current/components/http_foundation.html)。
步驟 4 — 以非同步方式產生 PDF
標題為「步驟 4 — 以非同步方式產生 PDF」的區段安裝 symfony/messenger 後,你就能將產生工作移出請求執行緒。
4a — 實作 builder
標題為「4a — 實作 builder」的區段實作 NextPDF\Symfony\Message\PdfBuilderInterface。handler 會傳入一份全新且已預先設定好的 Document,也會一併傳入訊息中可序列化的 context。
<?php
declare(strict_types=1);
namespace App\Pdf;
use NextPDF\Core\Document;use NextPDF\Symfony\Message\PdfBuilderInterface;
final class InvoicePdfBuilder implements PdfBuilderInterface{ public function build(Document $document, array $context): Document { $document->addPage(); $document->setFont('dejavusans', '', 12); $document->cell(0, 10, 'Invoice #' . $context['invoice_id']);
return $document; }}4b — 在 locator 中註冊 builder
標題為「4b — 在 locator 中註冊 builder」的區段handler 會透過以類別名稱為鍵的 PSR-11 服務 locator,resolve(解析)並取出 builder。因此,只有已註冊的 builder 才能被取用。請在 config/services.yaml 中把 builder 加入一個 locator:
services: App\Pdf\InvoicePdfBuilder: ~
nextpdf.pdf_builder_locator: class: Symfony\Component\DependencyInjection\ServiceLocator arguments: - 'App\Pdf\InvoicePdfBuilder': '@App\Pdf\InvoicePdfBuilder' tags: ['container.service_locator']
NextPDF\Symfony\Message\GeneratePdfHandler: arguments: $builderLocator: '@nextpdf.pdf_builder_locator'handler 會使用 builder 的 class-string id 向 locator 索取對應服務。PSR-11 容器識別碼是能唯一識別某個項目的字串(PSR-11 §1.1.2)。
4c — 分派訊息
標題為「4c — 分派訊息」的區段注入 Symfony\Component\Messenger\MessageBusInterface,接著分派訊息:
<?php
declare(strict_types=1);
namespace App\Controller;
use App\Pdf\InvoicePdfBuilder;use NextPDF\Symfony\Message\GeneratePdfMessage;use Symfony\Component\HttpFoundation\Response;use Symfony\Component\Messenger\MessageBusInterface;use Symfony\Component\Routing\Attribute\Route;
final class ReportController{ #[Route('/invoice/{id}/queue', name: 'invoice_queue')] public function queue(MessageBusInterface $bus, int $id): Response { $bus->dispatch(new GeneratePdfMessage( builderClass: InvoicePdfBuilder::class, outputPath: '/var/storage/invoices/' . $id . '.pdf', builderContext: ['invoice_id' => $id], ));
return new Response('PDF generation queued.', 202); }}GeneratePdfMessage 是一個 readonly DTO。它的建構式會拒絕空字串、非 .pdf 的輸出路徑、路徑穿越片段、串流包裝器協定,以及 null 位元組。它也要求 builderClass 必須是語法上合法的類別名稱。handler 會在執行階段、實際寫入前,再次驗證輸出路徑。因此,一個在分派時安全、但在消費時已不安全的路徑,仍然會被拒絕。#[AsMessageHandler] 屬性與 MessageBusInterface 的分派契約,皆遵循標準的 Symfony Messenger 模型(https://symfony.com/doc/current/messenger.html)。
4d — 路由訊息並執行 worker
標題為「4d — 路由訊息並執行 worker」的區段在 config/packages/messenger.yaml 中,將訊息路由到某個傳輸層:
framework: messenger: transports: async: '%env(MESSENGER_TRANSPORT_DSN)%' routing: NextPDF\Symfony\Message\GeneratePdfMessage: async接著執行一個 worker:
php bin/console messenger:consume async驗證是否運作正常
標題為「驗證是否運作正常」的區段php bin/console debug:container --tag=container.service_locatorphp bin/console messenger:consume async --limit=1 -vv第一個指令會確認 builder locator 已經註冊。第二個指令會消費一則已排入佇列的訊息,並印出 handler 的進度。
後續步驟
標題為「後續步驟」的區段- /integrations/symfony/configuration/ — 調整預設值、字型與文件服務。
- /integrations/symfony/production-usage/ — 負載下的 worker 安全性與串流處理。
- /integrations/symfony/troubleshooting/ — 常見的啟動與執行階段問題。
符合性
標題為「符合性」的區段每一列都是本頁面提出的規範性主張,並對應至受管理 SDO 語料庫中的完整 64 位元組十六進位 reference_id。provenance(來源資訊),包括語料庫資訊清單與檢索傳輸方式,記載於 _sidecars/rag-citations.yaml。
| 規範 | 條款 | 參考 ID | 主張 |
|---|---|---|---|
| PSR-11 | psr_11_container#1.1.2.p4 | 容器 has()/get() 識別碼契約 |
另請參閱
標題為「另請參閱」的區段- /integrations/symfony/overview/ — 功能摘要。
- /integrations/symfony/install/ — 安裝與註冊。
- /integrations/symfony/integration/ — 端對端串接參考。