跳转到内容

Laravel 开发者指南

Laravel 包让 NextPDF 遵循 Laravel 惯例,同时不改动核心文档生命周期。容器(container)拥有共享的注册表(registry)与工厂(factory)。每份 PDF 文档都是一次性使用的,只应被构建、返回、流式传输或保存一次。

当你围绕 nextpdf/laravel 设计应用程序服务、队列作业(queue job)、响应流程或测试覆盖时,请参考本指南。

层级拥有者职责不要放在这里
控制器(controller)应用程序授权请求、选择文档构建器(builder),并返回响应。跨使用场景共享的 PDF 版面规则。
应用程序服务应用程序收集领域数据,并调用构建文档的代码。容器启动逻辑或包配置。
文档构建器(builder)应用程序将领域数据转换为 NextPDF 文档调用。请求对象、Eloquent 查询逻辑,或队列传输(transport)细节。
Laravel 集成nextpdf/laravel绑定工厂、注册表、签名器(signer)、TSA 客户端、facade、响应与队列作业。业务特定的存储路径或租户策略。
核心引擎nextpdf/nextpdf构建并序列化 PDF。Laravel 响应、队列或文件系统策略。
阶段行为开发者动作
服务提供者注册NextPdfServiceProvider::register() 会注册共享注册表、文档工厂、文档绑定、HTTP 客户端、TSA 客户端、签名器,以及可选的电子发票契约。进入生产环境前,请先发布并检查 config/nextpdf.php
resolve(解析)文档Pdf facade 与 PdfDocumentInterface 绑定会通过 DocumentFactoryInterface 解析出一份全新文档。每个请求、命令或队列作业只解析一次文档。
撰写应用程序代码会通过 facade 或注入的文档,调用核心文档 API。请把领域数据的提取保持在文档构建器之外。
终端输出PdfResponse 会输出 HTTP 内容,或将文档保存到磁盘。每份文档只选择一条终端输出路径。
队列执行GeneratePdfJob 会在 worker 内重新构建文档,并再次验证输出路径。传入标量上下文数据,并让回调(callback)保持幂等。
路径用途
app/Pdf/Builders/*纯文档构建器。它们接收数据并返回一份已完成的文档。
app/Pdf/Data/*承载已授权文档输入的小型 DTO。
app/Services/*应用程序编排、查询、授权交接,以及存储路径选择。
app/Jobs/*当应用程序需要具名作业时,围绕 GeneratePdfJob 的可选包装器。
tests/Feature/Pdf/*HTTP 响应、队列派发与授权测试。
tests/Unit/Pdf/*以小型、具确定性输入进行的构建器测试。

让构建器与 Laravel 请求对象保持独立。构建器应该能用相同的输入,从控制器、命令、测试与队列 worker 中调用。

<?php
namespace App\Pdf\Builders;
use App\Pdf\Data\InvoicePdfData;
use NextPDF\Contracts\PdfDocumentInterface;
final readonly class InvoicePdfBuilder
{
public function build(PdfDocumentInterface $pdf, InvoicePdfData $data): PdfDocumentInterface
{
$pdf->setTitle($data->title)
->addPage()
->setFont('dejavusans', '', 12)
->writeHtml($data->html);
return $pdf;
}
}

当 PDF 流程属于应用程序逻辑的一部分时,请使用构造函数注入。只有在简短的控制器流程中,且静态写法能提升可读性时,才使用 facade。

<?php
namespace App\Http\Controllers;
use App\Pdf\Builders\InvoicePdfBuilder;
use App\Pdf\Data\InvoicePdfData;
use NextPDF\Contracts\PdfDocumentInterface;
use NextPDF\Laravel\Http\PdfResponse;
final readonly class DownloadInvoiceController
{
public function __invoke(
PdfDocumentInterface $pdf,
InvoicePdfBuilder $builder,
) {
$document = $builder->build(
$pdf,
InvoicePdfData::fromInvoiceId(1234),
);
return PdfResponse::download($document, 'invoice-1234.pdf');
}
}

响应辅助程序会在构建 Laravel 响应之前,先生成文档字节。它们是响应辅助程序,不是后台 renderer(渲染器)。

GeneratePdfJob 接受一个构建器可调用项与输出路径。该作业会在运行期验证不安全的路径。应用程序代码仍应在派发前,先选定对租户安全的存储根目录。

<?php
use App\Pdf\Builders\QueuedInvoiceBuilder;
use NextPDF\Laravel\Jobs\GeneratePdfJob;
GeneratePdfJob::dispatch(
outputPath: storage_path('app/pdfs/invoice-1234.pdf'),
builder: [QueuedInvoiceBuilder::class, 'build'],
)->onQueue(config('nextpdf.queue.queue', 'pdf'));

队列回调应该保持精简。与其把复杂的闭包(closure)存进队列负载,更建议由应用程序的作业监听器写入持久状态。

扩展点用途限制
PdfDocumentInterface 绑定替换或装饰文档创建流程,以应用整个应用程序的默认值。必须返回一个全新的文档实例。
DocumentFactoryInterface在服务与测试中明确创建全新文档。不要缓存返回的文档。
config/nextpdf.php默认值、队列设置、Chrome renderer(渲染器)设置、签名挂钩、TSA、OCSP 缓存。请把环境变量视为部署配置,而不是请求输入。
GeneratePdfJob 构建器以异步方式构建文档。可调用项必须能被 Laravel 的队列传输序列化。
成功/失败回调生成后的通知或清理。让回调保持幂等并留意副作用。
可选的 Premium 契约电子发票嵌入器、验证器、配置文件与 Schematron 执行器。只在已安装且已授权可选包的地方解析。
  1. 先在控制器或功能测试中同步构建第一份文档。
  2. 把版面代码搬进 app/Pdf/Builders 底下的构建器类。
  3. 把查询与授权逻辑搬进应用程序服务。
  4. PdfResponse 加上标头与文件名的测试。
  5. 把缓慢或大批量的生成作业移到 GeneratePdfJob
  6. 为序列化上下文、输出路径策略与失败处理加上队列测试。
  7. 使用具代表性的生产环境数据测量内存与 render 时间。
失败应该在哪里处理建议的响应
无效请求或未授权文档控制器或策略。返回常规应用程序的授权或验证响应。
缺少字体或图像无效构建器测试与应用程序日志记录。让请求或作业失败;不要输出不完整的 PDF。
不安全的输出路径应用程序存储服务与 GeneratePdfJob在派发前就拒绝,并以 worker 端验证作为纵深防御。
签名或 TSA 失败签名服务边界。决定文档是否允许未签名;对受监管的文档,默认采用 fail closed(失败即拒)。
队列超时队列 worker 配置与可观测性。只有在构建器具确定性,且输出路径可安全覆盖时,才重试。
关注点默认值何时覆盖
队列名称pdf当生成作业会与面向用户的作业争用资源时,请使用专用队列。
作业超时120只有在测量过文档大小与 worker 容量后,才调高。
响应文件名document.pdf使用经过净化的业务标识符。
字体注册表共享,并在预热后锁定。为热路径上使用的字体加入 preload_fonts
图像注册表共享的有界缓存。在内存受限的 worker 上调低 image_cache_mb
流式响应分块64 KB 的区块。不要依赖区块边界;那只是输出细节。
  • 控制器测试要断言 Content-TypeContent-Disposition 与防御性标头。
  • 构建器测试使用具确定性的 DTO,并且不查询数据库。
  • 队列测试要断言构建器收到的是一份全新文档。
  • 路径测试涵盖路径遍历、stream-wrapper、null 字节,以及非 .pdf 的拒绝。
  • worker 测试要在与生产环境相同的内存上限下,render 具代表性的文档。
  • 可选的签名测试要涵盖缺少证书、密码错误、TSA 无法使用,以及已配置的签名级别。