Руководство разработчика по Symfony
Пакет Symfony построен вокруг сервисов. Внедрите PdfFactory, вызывайте create() для каждого документа Portable Document Format (PDF) и используйте построители Messenger для асинхронной генерации. Фабрику можно держать сервисом контейнера, поскольку каждый вызов возвращает новый документ.
Используйте это руководство, когда проектируете контроллеры, сервисы, обработчики Messenger или точки расширения на уровне пакета для nextpdf/symfony.
Граница архитектуры
Заголовок раздела «Граница архитектуры»| Уровень | Кому принадлежит | Ответственность | Чего здесь быть не должно |
|---|---|---|---|
| Контроллер | Приложение | Авторизуйте запрос, соберите входные данные и верните PdfResponse. | Общий PDF-макет для нескольких сценариев использования. |
| Сервис приложения | Приложение | Загрузите доменные данные и выберите построитель. | Логика компилятора контейнера Symfony. |
| Сервис-построитель | Приложение | Реализуйте PdfBuilderInterface для синхронного построения документа или построения через очередь. | Объекты запроса, менеджеры сущностей или несериализуемый контекст. |
| Пакет Symfony | nextpdf/symfony | Регистрирует сервисы, дерево конфигурации, необязательный проход расширения, помощники для ответов и объекты передачи данных (DTO) для Messenger. | Политика хранения для конкретного арендатора. |
| Базовый движок | nextpdf/nextpdf | Создаёт и сериализует документ. | Поведение ответа Symfony или Messenger. |
Жизненный цикл выполнения
Заголовок раздела «Жизненный цикл выполнения»| Этап | Поведение | Действие разработчика |
|---|---|---|
| Загрузка пакета | NextPdfBundle::build() регистрирует обнаружение необязательного расширения. | Позвольте Symfony обнаружить пакет или зарегистрируйте его в bundles.php. |
| Загрузка конфигурации | NextPdfExtension::load() обрабатывает конфигурацию nextpdf: и загружает определения сервисов. | Задавайте конфигурацию явно и с учётом окружения. |
| Использование фабрики | PdfFactory::create() возвращает новый настроенный документ. | Не храните документы в сервисах. |
| Вывод контроллера | PdfResponse преобразует готовый документ в ответ. | Используйте помощник вместо ручной сборки заголовков. |
| Отправка через Messenger | GeneratePdfMessage содержит класс построителя, путь вывода и сериализуемый контекст. | Держите контекст минимальным и совместимым со скалярными значениями. |
| Обработка сообщения | GeneratePdfHandler получает построитель из локатора сервисов и сохраняет документ. | Делайте построители детерминированными и идемпотентными. |
Рекомендуемая структура приложения
Заголовок раздела «Рекомендуемая структура приложения»| Путь | Назначение |
|---|---|
src/Pdf/Builder/* | Сервисы, реализующие PdfBuilderInterface. |
src/Pdf/Data/* | Небольшие DTO или массивы, используемые как контекст построителя. |
src/Pdf/Storage/* | Выбор корня хранилища и политика имён выходных файлов. |
src/Controller/* | Точки входа для синхронных ответов. |
tests/Pdf/* | Тесты построителей, ответов, Messenger и конфигурации. |
Предпочитайте сервисы-построители статическим вспомогательным функциям. Их легко помечать тегами, декорировать, тестировать и использовать из 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'); }}Держите контекст контроллера небольшим. Если построителю нужно много доменных объектов, перенесите оркестрацию в сервис приложения и передайте построителю DTO или нормализованный массив.
Шаблон Messenger
Заголовок раздела «Шаблон Messenger»GeneratePdfMessage проверяет класс построителя и путь вывода перед отправкой. При запуске обработчик проверяет путь повторно.
<?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>', ],));Не помещайте сущности Doctrine, открытые потоки, замыкания, объекты запроса или объекты сервисов в builderContext.
Точки расширения
Заголовок раздела «Точки расширения»| Точка расширения | Используйте её для | Ограничение |
|---|---|---|
PdfFactory — декорирование сервиса | Применение значений по умолчанию приложения до того, как документы попадут в контроллеры. | Нужно сохранять семантику нового документа. |
PdfBuilderInterface | Определение построителей для документов, которые отправляются в очередь или используются повторно. | Должен возвращать Document. |
OptionalExtensionPass | Подключение необязательных возможностей Artisan или Premium во время компиляции. | Доступность определяется состоянием компиляции контейнера, а не состоянием запроса. |
| Дерево конфигурации Symfony | Значения по умолчанию, PDF/A, настройки рендерера, подпись, служба меток времени (TSA) и Messenger. | Недопустимая конфигурация должна приводить к ошибке во время сборки контейнера. |
GeneratePdfHandler — подключение сервиса | Ограничение набора построителей, доступных для сообщений из очереди. | Локатор сервисов должен предоставлять только утверждённые сервисы-построители. |
Рабочий процесс разработки
Заголовок раздела «Рабочий процесс разработки»- Добавьте сервис-построитель с детерминированными входными данными.
- Используйте
PdfFactory::create()в контроллере или сервисе. - Добавьте тест ответа для имени файла, типа содержимого и заголовков.
- Зарегистрируйте построитель в Messenger, когда тот же документ нужно генерировать асинхронно.
- Добавьте тесты недопустимых сообщений для имени класса, пути вывода и формы контекста.
- Добавьте тест компиляции контейнера с минимальной и производственной конфигурацией.
- Измеряйте время отрисовки и потребление памяти при тех же настройках PHP, что и в производственной среде.
Обработка сбоев
Заголовок раздела «Обработка сбоев»| Сбой | Где его следует обрабатывать | Рекомендуемый ответ |
|---|---|---|
| Недопустимая конфигурация | Компиляция контейнера. | Прервите развёртывание до того, как трафик дойдёт до приложения. |
| Отсутствует сервис-построитель | Тесты обработчика Messenger и теги сервисов. | Завершите сообщение с ошибкой и оповестите ответственную команду. |
| Небезопасный путь вывода | Конструктор сообщения и политика хранения. | Отклоните его до отправки; сохраните проверку в обработчике как дополнительный уровень защиты. |
| Необязательное расширение недоступно | Проход компилятора и поведение фабрики. | Отключите необязательную возможность или сделайте установку явной. |
| Сбой преобразования сервиса или отрисовки | Граница построителя. | По умолчанию завершайте с ошибкой, если для сценария не задокументирован запасной вариант. |
Безопасные значения по умолчанию
Заголовок раздела «Безопасные значения по умолчанию»| Аспект | По умолчанию | Когда переопределять |
|---|---|---|
| Время жизни фабрики | Сервис контейнера. | Оставьте как есть; фабрика безопасна, потому что создаёт новые документы. |
| Время жизни документа | Одна единица работы. | Никогда не используйте его совместно между запросами или сообщениями. |
| Проверка пути вывода | Конструктор сообщения и обработчик. | Добавьте ограничения по арендатору или корню хранилища в коде приложения. |
| Имя файла ответа | document.pdf. | Переопределите его очищенными бизнес-идентификаторами. |
| Транспорт Messenger | async. | Используйте выделенный транспорт, когда работа с PDF ресурсоёмкая. |
Контрольный список тестирования
Заголовок раздела «Контрольный список тестирования»- Тесты контейнера компилируют пакет с минимальной и производственной конфигурацией.
- Тесты ответов проверяют заголовки безопасности и обработку имени файла.
- Тесты Messenger проверяют, что недопустимые пути и недопустимые имена классов построителей приводят к ошибке до отправки.
- Тесты обработчика используют реальный сервис-построитель и временный каталог вывода.
- Тесты построителей отрисовывают репрезентативный документ и сохраняют его с правами доступа файловой системы, близкими к производственным.
- Тесты необязательных расширений охватывают случаи недоступности Artisan, недоступности Premium и поведение настроенного профиля PDF/A.