Руководство разработчика по Laravel
Пакет Laravel адаптирует NextPDF к соглашениям Laravel, не меняя жизненный цикл документа в ядре. Контейнер отвечает за общие реестры и фабрики. Каждый PDF-документ одноразовый: соберите его один раз, затем верните, передайте потоком или сохраните.
Используйте это руководство при проектировании сервисов приложения, заданий очереди, потоковых ответов или тестового покрытия для nextpdf/laravel.
Граница архитектуры
Заголовок раздела «Граница архитектуры»| Слой | Кому принадлежит | Зона ответственности | Чего сюда не помещать |
|---|---|---|---|
| Контроллер | Приложение | Авторизуйте запрос, выберите построитель документа и верните ответ. | Правила макета PDF, общие для разных сценариев использования. |
| Сервис приложения | Приложение | Соберите данные предметной области и вызовите код, который строит документ. | Логика загрузки контейнера или конфигурация пакета. |
| Построитель документа | Приложение | Преобразуйте данные предметной области в вызовы документа NextPDF. | Объекты запроса, логика запросов Eloquent или детали транспорта очереди. |
| Интеграция с Laravel | nextpdf/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. | Разрешайте их только там, где необязательный пакет установлен и лицензирован. |
Процесс разработки
Заголовок раздела «Процесс разработки»- Соберите первый документ синхронно в контроллере или функциональном тесте.
- Перенесите код макета в класс построителя в каталоге
app/Pdf/Builders. - Перенесите логику запросов и авторизации в сервис приложения.
- Добавьте тесты
PdfResponseдля заголовков и имён файлов. - Перенесите медленную или высокообъёмную генерацию в
GeneratePdfJob. - Добавьте тесты очереди для сериализованного контекста, политики пути вывода и обработки сбоев.
- Измерьте память и время отрисовки на репрезентативных данных из продакшена.
Обработка сбоев
Заголовок раздела «Обработка сбоев»| Сбой | Где его следует обрабатывать | Рекомендуемый ответ |
|---|---|---|
| Недопустимый запрос или неавторизованный документ | Контроллер или политика. | Верните обычный для приложения ответ авторизации или валидации. |
| Отсутствующий шрифт или недопустимое изображение | Тест построителя и журналирование приложения. | Завершайте запрос или задание сбоем; не выдавайте частичные PDF. |
| Небезопасный путь вывода | Сервис хранилища приложения и GeneratePdfJob. | Отклоняйте до диспетчеризации и полагайтесь на проверку на стороне обработчика как на дополнительный уровень защиты. |
| Сбой подписания или TSA | Граница сервиса подписания. | Решите, может ли документ остаться неподписанным; по умолчанию для регулируемых документов завершайте обработку с отказом (fail closed). |
| Тайм-аут очереди | Конфигурация обработчика очереди и наблюдаемость. | Повторяйте попытку только тогда, когда построитель детерминирован, а путь вывода безопасно перезаписывать. |
Безопасные значения по умолчанию
Заголовок раздела «Безопасные значения по умолчанию»| Аспект | По умолчанию | Когда переопределять |
|---|---|---|
| Имя очереди | pdf | Используйте выделенную очередь, когда генерация конкурирует с заданиями, обслуживающими пользовательские запросы. |
| Тайм-аут задания | 120 секунд | Увеличивайте только после измерения размера документа и пропускной способности обработчика. |
| Имя файла ответа | document.pdf | Используйте очищенные бизнес-идентификаторы. |
| Реестр шрифтов | Общий; после прогрева блокируется. | Добавьте preload_fonts для шрифтов, используемых на горячих путях. |
| Реестр изображений | Общий кэш с ограничением. | Уменьшите image_cache_mb для обработчиков с ограниченной памятью. |
| Разбиение потокового ответа на части | Части по 64 КБ. | Не полагайтесь на границы частей; это деталь вывода. |
Контрольный список тестирования
Заголовок раздела «Контрольный список тестирования»- Тесты контроллера проверяют
Content-Type,Content-Dispositionи заголовки безопасности. - Тесты построителей используют детерминированные DTO и не обращаются к базе данных.
- Тесты очереди проверяют, что построитель получает новый документ.
- Тесты путей охватывают обход каталогов, обёртки потоков, нулевой байт и отклонение путей без расширения
.pdf. - Тесты обработчика отрисовывают репрезентативные документы при том же лимите памяти, что и в продакшене.
- Необязательные тесты подписания охватывают отсутствующий сертификат, неверный пароль, недоступность TSA и настроенный уровень подписи.