ข้ามไปยังเนื้อหา

เริ่มต้นใช้งาน NextPDF Symfony แบบรวดเร็ว

Inject PdfFactory เพื่อสร้าง Document แล้วส่งคืนผ่าน PdfResponse เมื่อต้องการสร้างเอกสารในเบื้องหลัง ให้ dispatch GeneratePdfMessage ไปยัง transport ของ Messenger

Inject 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/pdf, disposition แบบ attachment, Content-Length และ security header ที่ bundle กำหนดไว้ตายตัว เอกสารของ Symfony อธิบายคลาส Response มาตรฐานและโมเดล header ไว้ที่ (https://symfony.com/doc/current/components/http_foundation.html)

ใช้ inline() เมื่อต้องการให้เบราว์เซอร์แสดง PDF แทนการดาวน์โหลด:

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

disposition จะเปลี่ยนเป็น inline ส่วน header อื่นทั้งหมดจะคงเดิม

สำหรับเอกสารขนาดใหญ่ ให้ใช้รูปแบบการสตรีม ซึ่งจะส่งออก PDF เป็นชังก์ขนาด 64 KB เพื่อช่วยลดการใช้หน่วยความจำสูงสุด โดยจะส่งคืน Symfony\Component\HttpFoundation\StreamedResponse และเว้น Content-Length ไว้

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

ใช้ streamInline() สำหรับการสตรีมแบบ inline เอกสารของ Symfony อธิบายสัญญาของ callback ของ StreamedResponse ว่าเป็น callable แบบ void ที่ flush เอาต์พุต (https://symfony.com/doc/current/components/http_foundation.html)

หลังจากติดตั้ง symfony/messenger แล้ว สามารถย้ายงานสร้างออกจากเธรดของคำขอได้

สร้าง implementation ของ NextPDF\Symfony\Message\PdfBuilderInterface handler จะมอบ Document ใหม่ที่กำหนดค่าไว้ล่วงหน้า พร้อม context ที่ serialize ได้จากข้อความให้กับ implementation นั้น

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;
}
}

handler จะ resolve builder จาก service locator ตาม PHP Standard Recommendation 11 (PSR-11) โดยใช้ชื่อคลาสเป็นคีย์ เฉพาะ builder ที่ลงทะเบียนแล้วเท่านั้นจึงจะเข้าถึงได้ เพิ่ม builder ลงใน locator ภายใน 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'

handler จะร้องขอ builder จาก locator ด้วย id ที่เป็น class-string ของ builder นั้น ใน PSR-11 ตัวระบุของ container คือสตริงที่ระบุรายการได้อย่างไม่ซ้ำกัน (PSR-11 §1.1.2)

Inject Symfony\Component\Messenger\MessageBusInterface แล้ว dispatch ข้อความ:

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);
}
}

GeneratePdfMessage เป็น data transfer object (DTO) แบบ readonly โดย constructor จะปฏิเสธพาธเอาต์พุตที่ว่างเปล่า ไม่ลงท้ายด้วย .pdf มีส่วนที่เป็น path-traversal ใช้สคีมแบบ stream-wrapper หรือมี null byte นอกจากนี้ยังบังคับให้ builderClass ต้องเป็นชื่อคลาสที่ถูกต้องตามไวยากรณ์ handler จะตรวจสอบพาธเอาต์พุตอีกครั้งตอนทำงานก่อนเขียน หากพาธปลอดภัยตอน dispatch แต่ไม่ปลอดภัยตอน consume handler ก็ยังคงปฏิเสธพาธนั้น แอตทริบิวต์ #[AsMessageHandler] และสัญญาการ dispatch ของ MessageBusInterface เป็นไปตามโมเดล Symfony Messenger มาตรฐาน (https://symfony.com/doc/current/messenger.html)

ใน config/packages/messenger.yaml ให้กำหนดเส้นทางข้อความไปยัง transport:

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

จากนั้นรัน worker:

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

คำสั่งแรกยืนยันว่า builder locator ถูกลงทะเบียนแล้ว คำสั่งที่สองจะ consume ข้อความในคิวหนึ่งข้อความและพิมพ์ความคืบหน้าของ handler

  • /integrations/symfony/configuration/ — ปรับค่าเริ่มต้น ฟอนต์ และ document service
  • /integrations/symfony/production-usage/ — ทบทวนความปลอดภัยของ worker และการสตรีมภายใต้ภาระงานสูง
  • /integrations/symfony/troubleshooting/ — แก้ปัญหาที่พบบ่อยตอน boot และ runtime

แต่ละแถวคือข้อความเชิงบรรทัดฐานที่ระบุในหน้านี้ และเชื่อมโยงกับ reference_id แบบ 64-hex เต็มจากคลังของ standards development organization (SDO) ที่มีการควบคุมการเข้าถึง _sidecars/rag-citations.yaml มีข้อมูลแหล่งที่มา รวมถึง manifest ของคลังและ transport สำหรับการเรียกข้อมูล

ข้อกำหนดข้อรหัสอ้างอิง (reference_id)ข้อความที่อ้าง
PSR-11psr_11_container#1.1.2.p4สัญญาตัวระบุของ has()/get() ของ Container
  • /integrations/symfony/overview/ — ทบทวนสรุปความสามารถ
  • /integrations/symfony/install/ — ติดตั้งและลงทะเบียน bundle
  • /integrations/symfony/integration/ — ทบทวนเอกสารอ้างอิงการเชื่อมต่อแบบ end-to-end