コンテンツにスキップ

NextPDF Symfony クイックスタート

まず PdfFactory をインジェクトして Document を構築し、PdfResponse で返します。バックグラウンドで PDF を生成する場合は、GeneratePdfMessage を Messenger トランスポートにディスパッチします。

まず NextPDF\Symfony\Service\PdfFactory をインジェクトします。その create() メソッドは、新しい NextPDF\Core\Document を返します。設定済みのデフォルト値(作成者、著者、言語)はあらかじめ適用されています。そのドキュメントを NextPDF\Symfony\Http\PdfResponse で返します。

src/Controller/InvoiceController.php
<?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/pdfattachment ディスポジション、Content-Length、およびバンドルの固定セキュリティヘッダーが含まれます。Symfony では、標準の Response クラスとそのヘッダーモデルが文書化されています(https://symfony.com/doc/current/components/http_foundation.html)。

PDF をダウンロードさせる代わりにブラウザーで表示するには、inline() を使用します。

return PdfResponse::inline($doc, 'preview.pdf');

ディスポジションは inline になります。その他のヘッダーはすべて変わりません。

大きなドキュメントでは、ストリーミング版が PDF を 64 KB のチャンクで出力します。これにより、ピークメモリを削減できます。これらのメソッドは Symfony\Component\HttpFoundation\StreamedResponse を返し、Content-Length を省略します。

return PdfResponse::streamDownload($doc, 'annual-report.pdf');

streamInline() はインライン表示に対応するメソッドです。Symfony では StreamedResponse のコールバックコントラクトが文書化されており、これは出力をフラッシュする void の callable です(https://symfony.com/doc/current/components/http_foundation.html)。

お使いの環境に symfony/messenger がインストールされている場合、生成処理をリクエストスレッドから切り離すことができます。

まず NextPDF\Symfony\Message\PdfBuilderInterface を実装します。ハンドラーは、設定済みの新しい Document を渡します。また、メッセージから取り出したシリアライズ可能なコンテキストも渡します。

src/Pdf/InvoicePdfBuilder.php
<?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;
}
}

ハンドラーは、クラス名をキーとする PSR-11 サービスロケーターからビルダーを resolve(解決)します。そのため、登録済みのビルダーだけにアクセスできます。config/services.yaml で、ロケーターにビルダーを追加します。

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'

ハンドラーは、クラス文字列の ID によってロケーターにビルダーを要求します。PSR-11 におけるコンテナー識別子は、エントリーを一意に識別する文字列です(PSR-11 §1.1.2)。

まず Symfony\Component\Messenger\MessageBusInterface をインジェクトし、メッセージをディスパッチします。

src/Controller/ReportController.php
<?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);
}
}

GeneratePdfMessagereadonly の DTO です。そのコンストラクターは、空の出力パス、.pdf 以外の出力パス、パストラバーサルのセグメント、ストリームラッパーのスキーム、および null バイトを拒否します。また、builderClass が構文上有効なクラス名であることも要求します。ハンドラーは書き込み前に、実行時にも出力パスを再検証します。そのため、ディスパッチ時には安全でも消費時に安全でなくなったパスは、依然として拒否されます。#[AsMessageHandler] 属性と MessageBusInterface のディスパッチコントラクトは、標準の Symfony Messenger モデルに従います(https://symfony.com/doc/current/messenger.html)。

設定ファイル config/packages/messenger.yaml で、メッセージをトランスポートにルーティングします。

framework:
messenger:
transports:
async: '%env(MESSENGER_TRANSPORT_DSN)%'
routing:
NextPDF\Symfony\Message\GeneratePdfMessage: async

次に、ワーカーを実行します。

Terminal window
php bin/console messenger:consume async
Terminal window
php bin/console debug:container --tag=container.service_locator
php bin/console messenger:consume async --limit=1 -vv

最初のコマンドは、ビルダーロケーターが登録されていることを確認します。2 番目のコマンドは、キューに入った 1 件のメッセージを消費し、ハンドラーの進行状況を出力します。

  • /integrations/symfony/configuration/ — デフォルト値、フォント、およびドキュメントサービスを調整します。
  • /integrations/symfony/production-usage/ — ワーカーの安全性と高負荷時のストリーミング。
  • /integrations/symfony/troubleshooting/ — 一般的な起動時および実行時の問題。

各行は、このページで行われる規範的な主張であり、ゲート付き SDO コーパスの完全な 64 桁の 16 進数 reference_id に固定されています。来歴(コーパスマニフェスト、取得トランスポート)は _sidecars/rag-citations.yaml にあります。

仕様条項リファレンス ID主張
PSR-11psr_11_container#1.1.2.p4コンテナーの has()/get() 識別子コントラクト
  • /integrations/symfony/overview/ — 機能の概要。
  • /integrations/symfony/install/ — インストールと登録。
  • /integrations/symfony/integration/ — エンドツーエンドの結線リファレンス。