Перейти к содержимому

Руководство разработчика по Laravel

Пакет Laravel адаптирует NextPDF к соглашениям Laravel, не меняя жизненный цикл документа в ядре. Контейнер отвечает за общие реестры и фабрики. Каждый PDF-документ одноразовый: соберите его один раз, затем верните, передайте потоком или сохраните.

Используйте это руководство при проектировании сервисов приложения, заданий очереди, потоковых ответов или тестового покрытия для nextpdf/laravel.

СлойКому принадлежитЗона ответственностиЧего сюда не помещать
КонтроллерПриложениеАвторизуйте запрос, выберите построитель документа и верните ответ.Правила макета PDF, общие для разных сценариев использования.
Сервис приложенияПриложениеСоберите данные предметной области и вызовите код, который строит документ.Логика загрузки контейнера или конфигурация пакета.
Построитель документаПриложениеПреобразуйте данные предметной области в вызовы документа NextPDF.Объекты запроса, логика запросов Eloquent или детали транспорта очереди.
Интеграция с Laravelnextpdf/laravelПривязка фабрик, реестров, подписывающего модуля, клиента TSA, фасада, ответов и задания очереди.Специфичные для бизнеса пути хранения или политика арендатора.
Ядро движкаnextpdf/nextpdfСборка и сериализация PDF.Политика ответа Laravel, очереди или файловой системы.
ЭтапПоведениеДействие разработчика
Регистрация поставщика сервисовNextPdfServiceProvider::register() регистрирует общие реестры, фабрику документов, привязку документа, HTTP-клиент, клиент службы меток времени (TSA), подписывающий модуль и необязательные контракты электронных счетов.Опубликуйте и проверьте config/nextpdf.php перед выпуском в продакшен.
Разрешение документаФасад Pdf и привязка PdfDocumentInterface разрешают новый документ через DocumentFactoryInterface.Разрешайте документ один раз на запрос, команду или задание в очереди.
СозданиеКод приложения вызывает API документов ядра через фасад или внедрённый документ.Держите извлечение данных предметной области вне построителя документа.
Конечный выводPdfResponse отдаёт HTTP-вывод, либо документ сохраняется на диск.Выберите один способ конечного вывода для каждого документа.
Выполнение в очередиGeneratePdfJob заново собирает документ в обработчике и снова проверяет путь вывода.Передавайте скалярный контекст и сохраняйте идемпотентность обратных вызовов.
ПутьНазначение
app/Pdf/Builders/*Чистые построители документов. Они получают данные и возвращают готовый документ.
app/Pdf/Data/*Небольшие объекты передачи данных (DTO), которые несут уже авторизованный ввод документа.
app/Services/*Оркестрация приложения, запросы, авторизация и выбор пути хранения.
app/Jobs/*Необязательные обёртки вокруг GeneratePdfJob, когда приложению нужны именованные задания.
tests/Feature/Pdf/*Тесты HTTP-ответа, диспетчеризации очереди и авторизации.
tests/Unit/Pdf/*Тесты построителей с небольшим детерминированным вводом.

Держите построители независимыми от объектов запроса Laravel. Построитель должен одинаково вызываться из контроллера, команды, теста или обработчика очереди с одними и теми же входными данными.

<?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 входит в логику приложения. Используйте фасад только для коротких сценариев в контроллере, где статический стиль читается легче.

<?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. Это именно методы для ответов, а не фоновые рендереры.

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

Держите обратные вызовы очереди небольшими. Предпочитайте записывать долговременное состояние в слушателе заданий приложения, а не хранить сложные замыкания в полезной нагрузке очереди.

Точка расширенияДля чего использоватьОграничение
PdfDocumentInterface (привязка)Замените или декорируйте создание документа для общих для всего приложения значений по умолчанию.Должен возвращаться новый экземпляр документа.
DocumentFactoryInterfaceСоздавайте новые документы явно в сервисах и тестах.Не кэшируйте возвращённые документы.
config/nextpdf.phpЗначения по умолчанию, настройки очереди, настройки рендерера Chrome, хуки подписания, TSA, кэш OCSP.Рассматривайте переменные окружения как конфигурацию развёртывания, а не как ввод запроса.
GeneratePdfJob (построитель)Стройте документы асинхронно.Вызываемый объект должен быть сериализуемым транспортом очереди Laravel.
Обратные вызовы успеха и сбояУведомления после генерации или очистка.Сохраняйте обратные вызовы идемпотентными и учитывайте побочные эффекты.
Необязательные контракты PremiumВстраиватель электронных счетов, валидатор, профиль и средство запуска Schematron.Разрешайте их только там, где необязательный пакет установлен и лицензирован.
  1. Соберите первый документ синхронно в контроллере или функциональном тесте.
  2. Перенесите код макета в класс построителя в каталоге app/Pdf/Builders.
  3. Перенесите логику запросов и авторизации в сервис приложения.
  4. Добавьте тесты PdfResponse для заголовков и имён файлов.
  5. Перенесите медленную или высокообъёмную генерацию в GeneratePdfJob.
  6. Добавьте тесты очереди для сериализованного контекста, политики пути вывода и обработки сбоев.
  7. Измерьте память и время отрисовки на репрезентативных данных из продакшена.
СбойГде его следует обрабатыватьРекомендуемый ответ
Недопустимый запрос или неавторизованный документКонтроллер или политика.Верните обычный для приложения ответ авторизации или валидации.
Отсутствующий шрифт или недопустимое изображениеТест построителя и журналирование приложения.Завершайте запрос или задание сбоем; не выдавайте частичные PDF.
Небезопасный путь выводаСервис хранилища приложения и GeneratePdfJob.Отклоняйте до диспетчеризации и полагайтесь на проверку на стороне обработчика как на дополнительный уровень защиты.
Сбой подписания или TSAГраница сервиса подписания.Решите, может ли документ остаться неподписанным; по умолчанию для регулируемых документов завершайте обработку с отказом (fail closed).
Тайм-аут очередиКонфигурация обработчика очереди и наблюдаемость.Повторяйте попытку только тогда, когда построитель детерминирован, а путь вывода безопасно перезаписывать.
АспектПо умолчаниюКогда переопределять
Имя очередиpdfИспользуйте выделенную очередь, когда генерация конкурирует с заданиями, обслуживающими пользовательские запросы.
Тайм-аут задания120 секундУвеличивайте только после измерения размера документа и пропускной способности обработчика.
Имя файла ответаdocument.pdfИспользуйте очищенные бизнес-идентификаторы.
Реестр шрифтовОбщий; после прогрева блокируется.Добавьте preload_fonts для шрифтов, используемых на горячих путях.
Реестр изображенийОбщий кэш с ограничением.Уменьшите image_cache_mb для обработчиков с ограниченной памятью.
Разбиение потокового ответа на частиЧасти по 64 КБ.Не полагайтесь на границы частей; это деталь вывода.
  • Тесты контроллера проверяют Content-Type, Content-Disposition и заголовки безопасности.
  • Тесты построителей используют детерминированные DTO и не обращаются к базе данных.
  • Тесты очереди проверяют, что построитель получает новый документ.
  • Тесты путей охватывают обход каталогов, обёртки потоков, нулевой байт и отклонение путей без расширения .pdf.
  • Тесты обработчика отрисовывают репрезентативные документы при том же лимите памяти, что и в продакшене.
  • Необязательные тесты подписания охватывают отсутствующий сертификат, неверный пароль, недоступность TSA и настроенный уровень подписи.