Skip to content

Symfony developer guide

The Symfony package puts services first. Inject PdfFactory, call create() for each Portable Document Format (PDF) document, and use Messenger builders for asynchronous generation. You can keep the factory as a container service because each call returns a fresh document.

Use this guide when you design controllers, services, Messenger handlers, or bundle-level extension points for nextpdf/symfony.

LayerOwned byResponsibilityDo not put here
ControllerApplicationAuthorize the request, collect input, and return PdfResponse.PDF layout shared across use cases.
Application serviceApplicationLoad domain data and select a builder.Symfony container compiler logic.
Builder serviceApplicationImplement PdfBuilderInterface for synchronous or queued document construction.Request objects, entity managers, or non-serializable context.
Symfony bundlenextpdf/symfonyRegister services, the config tree, optional extension pass, response helpers, and Messenger data transfer objects (DTOs).Tenant-specific storage policy.
Core enginenextpdf/nextpdfCreate and serialize the document.Symfony response or Messenger behavior.
StageBehaviorDeveloper action
Bundle bootNextPdfBundle::build() registers optional extension detection.Let Symfony discover the bundle or register it in bundles.php.
Config loadNextPdfExtension::load() processes nextpdf: config and loads service definitions.Keep configuration explicit and environment-aware.
Factory usePdfFactory::create() returns a fresh, configured document.Do not store documents in services.
Controller outputPdfResponse turns a completed document into a response.Use the helper instead of assembling headers manually.
Messenger dispatchGeneratePdfMessage carries builder class, output path, and serializable context.Keep context minimal and scalar-friendly.
Message handlingGeneratePdfHandler resolves the builder from a service locator and saves the document.Make builders deterministic and idempotent.
PathPurpose
src/Pdf/Builder/*Services that implement PdfBuilderInterface.
src/Pdf/Data/*Small DTOs or arrays used as builder context.
src/Pdf/Storage/*Storage-root selection and output filename policy.
src/Controller/*Synchronous response entry points.
tests/Pdf/*Builder, response, Messenger, and config tests.

Prefer builder services over static helper functions. They are easy to tag, decorate, test, and use from 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');
}
}

Keep controller context small. When a builder needs many domain objects, move orchestration into an application service and pass a DTO or normalized array to the builder.

GeneratePdfMessage validates the builder class and output path before dispatch. The handler validates the path again when it runs.

<?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>',
],
));

Do not put Doctrine entities, open streams, closures, request objects, or service objects in builderContext.

Extension pointUse it forConstraint
PdfFactory service decorationApplying application defaults before documents reach controllers.Must preserve fresh-document semantics.
PdfBuilderInterfaceDefining queued or reusable document builders.Must return a Document.
OptionalExtensionPassEnabling optional Artisan or Premium features at compile time.Availability is container compile state, not request state.
Symfony config treeDefaults, PDF/A, renderer settings, signature, time-stamping authority (TSA), and Messenger.Invalid config should fail during container build.
GeneratePdfHandler service wiringRestricting which builders are reachable from queued messages.Service locator should expose only approved builder services.
  1. Add a builder service with deterministic input.
  2. Use PdfFactory::create() in a controller or service.
  3. Add a response test for the filename, content type, and headers.
  4. Register the builder with Messenger when the same document must be generated asynchronously.
  5. Add invalid-message tests for the class name, output path, and context shape.
  6. Add a container compilation test with minimal and production configuration.
  7. Measure render time and memory under the same PHP settings as production.
FailureWhere it should be handledRecommended response
Invalid configContainer compilation.Fail deployment before traffic reaches the app.
Missing builder serviceMessenger handler tests and service tags.Fail the message and alert the owning team.
Unsafe output pathMessage constructor and storage policy.Reject it before dispatch; keep handler validation as defense in depth.
Optional extension unavailableCompiler pass and factory behavior.Disable optional feature or make installation explicit.
Service conversion or render failureBuilder boundary.Fail closed unless the use case has a documented fallback.
ConcernDefaultWhen to override
Factory lifetimeContainer service.Keep this; the factory is safe because it creates fresh documents.
Document lifetimeOne unit of work.Never share across requests or messages.
Output path validationMessage constructor and handler.Add tenant or storage-root constraints in application code.
Response filenamedocument.pdf.Override with sanitized business identifiers.
Messenger transportasync.Use a dedicated transport when PDF work is heavy.
  • Container tests compile the bundle with minimal and production configuration.
  • Response tests assert security headers and filename handling.
  • Messenger tests assert invalid paths and invalid builder class names fail before dispatch.
  • Handler tests use a real builder service and a temporary output directory.
  • Builder tests render a representative document and save it under production-like filesystem permissions.
  • Optional extension tests cover Artisan unavailable, Premium unavailable, and configured PDF/A profile behavior.