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

คู่มือนักพัฒนา Symfony

แพ็กเกจ Symfony ยึดเซอร์วิสเป็นหลัก ให้ฉีด PdfFactory เรียก create() สำหรับเอกสาร Portable Document Format (PDF) แต่ละฉบับ และใช้ Messenger builder สำหรับการสร้างแบบอะซิงโครนัส คุณสามารถเก็บ factory ไว้เป็นเซอร์วิสในคอนเทนเนอร์ได้ เพราะการเรียกแต่ละครั้งจะคืนเอกสารฉบับใหม่

ใช้คู่มือนี้สำหรับ nextpdf/symfony เมื่อออกแบบ controller, เซอร์วิส, Messenger handler หรือจุดขยาย (extension point) ระดับ bundle

เลเยอร์เป็นเจ้าของโดยความรับผิดชอบสิ่งที่ไม่ควรใส่ที่นี่
Controllerแอปพลิเคชันตรวจสอบสิทธิ์คำขอ รวบรวมข้อมูลนำเข้า และคืนค่า PdfResponseเค้าโครง PDF ที่ใช้ร่วมกันในหลายกรณีการใช้งาน
เซอร์วิสของแอปพลิเคชันแอปพลิเคชันโหลดข้อมูลโดเมนและเลือก builderตรรกะของ Symfony container compiler
เซอร์วิส builderแอปพลิเคชันใช้งาน PdfBuilderInterface เพื่อสร้างเอกสารแบบซิงโครนัสหรือแบบเข้าคิวออบเจกต์คำขอ, entity manager หรือบริบทที่ไม่สามารถซีเรียลไลซ์ได้
Symfony bundlenextpdf/symfonyลงทะเบียนเซอร์วิส ผัง config, extension pass ที่เลือกได้, response helper และ Messenger data transfer object (DTO)นโยบายการจัดเก็บเฉพาะผู้เช่า (tenant)
เอนจินหลักnextpdf/nextpdfสร้างและซีเรียลไลซ์เอกสารพฤติกรรมของ Symfony response หรือ Messenger
ขั้นตอนพฤติกรรมการดำเนินการของนักพัฒนา
การบูต bundleNextPdfBundle::build() ลงทะเบียนการตรวจหา extension ที่เลือกได้ให้ Symfony ค้นพบ bundle เอง หรือจะลงทะเบียน bundle ไว้ใน bundles.php ก็ได้
การโหลด configNextPdfExtension::load() ประมวลผล config ของ nextpdf: และโหลดนิยามเซอร์วิสกำหนดค่าให้ชัดเจนและสอดคล้องกับสภาพแวดล้อม
การใช้ factoryPdfFactory::create() คืนเอกสารฉบับใหม่ที่กำหนดค่าแล้วอย่าเก็บเอกสารไว้ในเซอร์วิส
เอาต์พุตของ controllerPdfResponse แปลงเอกสารที่สร้างเสร็จให้เป็น responseใช้ helper แทนการประกอบ header ด้วยตนเอง
การส่งงานผ่าน MessengerGeneratePdfMessage บรรจุคลาส builder, พาธเอาต์พุต และบริบทที่ซีเรียลไลซ์ได้เก็บบริบทให้เล็กที่สุดและใช้ค่าสเกลาร์เป็นหลัก
การจัดการข้อความGeneratePdfHandler ค้นหา builder ผ่าน service locator และบันทึกเอกสารทำให้ builder มีผลลัพธ์ที่กำหนดได้แน่นอน (deterministic) และทำซ้ำได้โดยไม่เปลี่ยนผลลัพธ์ (idempotent)
พาธวัตถุประสงค์
src/Pdf/Builder/*เซอร์วิสที่ใช้งานอินเทอร์เฟซ PdfBuilderInterface
src/Pdf/Data/*DTO ขนาดเล็กหรืออาร์เรย์สำหรับใช้เป็นบริบทของ builder
src/Pdf/Storage/*การเลือกรากที่จัดเก็บ (storage root) และนโยบายชื่อไฟล์เอาต์พุต
src/Controller/*จุดเข้าสำหรับ response แบบซิงโครนัส
tests/Pdf/*การทดสอบ builder, response, Messenger และ config ต่างๆ

ควรใช้เซอร์วิส builder มากกว่าฟังก์ชัน helper แบบสแตติก เซอร์วิสเหล่านี้ติดแท็ก ตกแต่ง (decorate) ทดสอบ และเรียกใช้จาก 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 ให้เล็ก เมื่อ builder ต้องการออบเจกต์โดเมนจำนวนมาก ให้ย้ายการประสานงาน (orchestration) ไปไว้ในเซอร์วิสของแอปพลิเคชัน แล้วส่ง 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>',
],
));

ภายใน builderContext อย่าใส่ Doctrine entity, สตรีมที่เปิดอยู่, closure, ออบเจกต์คำขอ หรือออบเจกต์เซอร์วิส

จุดขยายใช้สำหรับข้อจำกัด
PdfFactory สำหรับการตกแต่ง (decoration) เซอร์วิสการใช้ค่าเริ่มต้นของแอปพลิเคชันก่อนส่งเอกสารถึง controllerต้องรักษาความหมายของการคืนเอกสารฉบับใหม่ (fresh-document) ไว้
PdfBuilderInterfaceการนิยาม builder เอกสารแบบเข้าคิวหรือแบบนำกลับมาใช้ใหม่ได้ต้องคืนค่าเป็น Document กลับมา
OptionalExtensionPassการเปิดใช้คุณสมบัติ Artisan หรือ Premium ที่เลือกได้ในเวลาคอมไพล์ความพร้อมใช้งานเป็นสถานะของการคอมไพล์คอนเทนเนอร์ ไม่ใช่สถานะของคำขอ
ผัง config ของ Symfonyค่าเริ่มต้น, PDF/A, การตั้งค่าตัวเรนเดอร์, ลายเซ็น, time-stamping authority (TSA) และ Messengerconfig ที่ไม่ถูกต้องควรล้มเหลวระหว่างการสร้างคอนเทนเนอร์
GeneratePdfHandler สำหรับการเชื่อมต่อเซอร์วิสการจำกัดว่า builder ใดเข้าถึงได้จากข้อความที่เข้าคิวservice locator ควรเปิดเผยเฉพาะเซอร์วิส builder ที่ได้รับอนุมัติเท่านั้น
  1. เพิ่มเซอร์วิส builder ที่รับข้อมูลนำเข้าแบบกำหนดได้แน่นอน
  2. ใช้ PdfFactory::create() ใน controller หรือเซอร์วิส
  3. เพิ่มการทดสอบ response สำหรับชื่อไฟล์ ประเภทเนื้อหา และ header
  4. ลงทะเบียน builder กับ Messenger เมื่อจำเป็นต้องสร้างเอกสารเดียวกันแบบอะซิงโครนัส
  5. เพิ่มการทดสอบข้อความที่ไม่ถูกต้องสำหรับชื่อคลาส พาธเอาต์พุต และรูปแบบบริบท
  6. เพิ่มการทดสอบการคอมไพล์คอนเทนเนอร์ด้วยการกำหนดค่าขั้นต่ำและการกำหนดค่าสำหรับใช้งานจริง
  7. วัดเวลาในการเรนเดอร์และหน่วยความจำภายใต้การตั้งค่า PHP เดียวกับการใช้งานจริง
ความล้มเหลวตำแหน่งที่ควรจัดการการตอบสนองที่แนะนำ
config ที่ไม่ถูกต้องการคอมไพล์คอนเทนเนอร์ทำให้การ deploy ล้มเหลวก่อนที่ทราฟฟิกจะถึงแอป
เซอร์วิส builder ที่ขาดหายไปการทดสอบ Messenger handler และแท็กเซอร์วิสทำให้ข้อความล้มเหลวและแจ้งเตือนทีมที่รับผิดชอบ
พาธเอาต์พุตที่ไม่ปลอดภัยตัวสร้าง (constructor) ของข้อความและนโยบายการจัดเก็บปฏิเสธก่อนส่งงาน และคงการตรวจสอบใน handler ไว้เพื่อป้องกันเชิงลึก
extension ที่เลือกได้ไม่พร้อมใช้งานcompiler pass และพฤติกรรมของ factoryปิดใช้คุณสมบัติที่เลือกได้ หรือทำให้ขั้นตอนการติดตั้งชัดเจน
การ resolve เซอร์วิสหรือการเรนเดอร์ล้มเหลวขอบเขตของ builderล้มเหลวแบบปิด (fail closed) เว้นแต่กรณีการใช้งานจะมีกลไกสำรอง (fallback) ที่จัดทำเป็นเอกสารไว้
ประเด็นค่าเริ่มต้นเมื่อใดควรแทนที่
อายุการใช้งานของ factoryเซอร์วิสในคอนเทนเนอร์คงค่านี้ไว้ เพราะ factory ปลอดภัยเนื่องจากสร้างเอกสารฉบับใหม่ทุกครั้ง
อายุการใช้งานของเอกสารหนึ่งรอบงาน (unit of work)อย่าใช้ร่วมกันข้ามคำขอหรือข้อความ
การตรวจสอบพาธเอาต์พุตตัวสร้างของข้อความและ handlerเพิ่มข้อจำกัดของผู้เช่า (tenant) หรือรากที่จัดเก็บในโค้ดของแอปพลิเคชัน
ชื่อไฟล์ของ responsedocument.pdfแทนที่ด้วยตัวระบุทางธุรกิจที่ผ่านการทำให้ปลอดภัย (sanitized) แล้ว
การขนส่ง (transport) ของ Messengerasyncใช้การขนส่งเฉพาะเมื่องาน PDF มีปริมาณมาก
  • ทดสอบว่าคอนเทนเนอร์คอมไพล์ bundle ได้ด้วยการกำหนดค่าขั้นต่ำและการกำหนดค่าสำหรับใช้งานจริง
  • การทดสอบ response ยืนยัน security header และการจัดการชื่อไฟล์
  • การทดสอบ Messenger ยืนยันว่าพาธที่ไม่ถูกต้องและชื่อคลาส builder ที่ไม่ถูกต้องจะล้มเหลวก่อนการส่งงาน
  • การทดสอบ handler ใช้เซอร์วิส builder จริงและไดเรกทอรีเอาต์พุตชั่วคราว
  • การทดสอบ builder เรนเดอร์เอกสารตัวแทน และบันทึกภายใต้สิทธิ์ของระบบไฟล์ที่คล้ายกับการใช้งานจริง
  • การทดสอบ extension ที่เลือกได้ครอบคลุมกรณี Artisan ไม่พร้อมใช้งาน, Premium ไม่พร้อมใช้งาน และพฤติกรรมของโปรไฟล์ PDF/A ที่กำหนดค่าไว้