Laravel developer guide
At a glance
Section titled “At a glance”The Laravel package adapts NextPDF to Laravel conventions without changing the core document lifecycle. The container owns shared registries and factories. Each PDF document is disposable, so you should build, return, stream, or save it once.
Use this guide when you design application services, queue jobs, response flows, or test coverage for nextpdf/laravel.
Architecture boundary
Section titled “Architecture boundary”| Layer | Owned by | Responsibility | Do not put here |
|---|---|---|---|
| Controller | Application | Authorize the request, choose a document builder, and return a response. | PDF layout rules shared across use cases. |
| Application service | Application | Collect domain data and call document-building code. | Container boot logic or package configuration. |
| Document builder | Application | Translate domain data into NextPDF document calls. | Request objects, Eloquent query logic, or queue transport details. |
| Laravel integration | nextpdf/laravel | Bind factories, registries, signer, TSA client, facade, responses, and queue job. | Business-specific storage paths or tenant policy. |
| Core engine | nextpdf/nextpdf | Build and serialize the PDF. | Laravel response, queue, or filesystem policy. |
Runtime lifecycle
Section titled “Runtime lifecycle”| Stage | Behavior | Developer action |
|---|---|---|
| Service provider registration | NextPdfServiceProvider::register() registers shared registries, the document factory, the document binding, HTTP client, time-stamping authority (TSA) client, signer, and optional e-invoice contracts. | Publish and review config/nextpdf.php before production. |
| Document resolution | The Pdf facade and PdfDocumentInterface binding resolve a fresh document through DocumentFactoryInterface. | Resolve a document once per request, command, or queued job. |
| Authoring | Application code calls core document APIs through the facade or injected document. | Keep domain data extraction outside the document builder. |
| Terminal output | PdfResponse emits HTTP output, or the document is saved to disk. | Choose one terminal output path per document. |
| Queue execution | GeneratePdfJob rebuilds the document in the worker and validates the output path again. | Pass scalar context and keep callbacks idempotent. |
Recommended application structure
Section titled “Recommended application structure”| Path | Purpose |
|---|---|
app/Pdf/Builders/* | Pure document builders. They receive data and return a completed document. |
app/Pdf/Data/* | Small data transfer objects (DTOs) that carry already-authorized document input. |
app/Services/* | Application orchestration, queries, authorization handoff, and storage path selection. |
app/Jobs/* | Optional wrappers around GeneratePdfJob when the application needs named jobs. |
tests/Feature/Pdf/* | HTTP response, queue dispatch, and authorization tests. |
tests/Unit/Pdf/* | Builder tests with small deterministic input. |
Keep builders independent of Laravel request objects. You should be able to call a builder from a controller, command, test, or queue worker with the same input.
<?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; }}Synchronous response pattern
Section titled “Synchronous response pattern”Use constructor injection when the PDF flow is part of application logic. Use the facade only for short controller flows where the static style is easier to read.
<?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'); }}Response helpers materialize the document bytes before they build the Laravel response. They are response helpers, not background renderers.
Queue pattern
Section titled “Queue pattern”GeneratePdfJob accepts a builder callable and an output path. The job validates unsafe paths at execution time. Application code should still choose a tenant-safe storage root before dispatch.
<?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'));Keep queue callbacks small. Prefer writing durable state from an application job listener instead of storing complex closures in the queue payload.
Extension points
Section titled “Extension points”| Extension point | Use it for | Constraint |
|---|---|---|
PdfDocumentInterface binding | Replace or decorate document creation for application-wide defaults. | Must return a fresh document instance. |
DocumentFactoryInterface | Create fresh documents explicitly in services and tests. | Do not cache returned documents. |
config/nextpdf.php | Defaults, queue settings, Chrome renderer settings, signing hooks, TSA, OCSP cache. | Treat environment variables as deployment config, not request input. |
GeneratePdfJob builder | Build documents asynchronously. | The callable must be serializable by Laravel’s queue transport. |
| Success/failure callbacks | Post-generation notification or cleanup. | Keep callbacks idempotent and side-effect aware. |
| Optional Premium contracts | E-invoice embedder, validator, profile, and Schematron runner. | Resolve only where the optional package is installed and licensed. |
Development workflow
Section titled “Development workflow”- Build the first document synchronously in a controller or feature test.
- Move layout code into a builder class under
app/Pdf/Builders. - Move query and authorization logic into an application service.
- Add
PdfResponsetests for headers and filenames. - Move slow or high-volume generation to
GeneratePdfJob. - Add queue tests for serialized context, output path policy, and failure handling.
- Measure memory and render time with representative production data.
Failure handling
Section titled “Failure handling”| Failure | Where it should be handled | Recommended response |
|---|---|---|
| Invalid request or unauthorized document | Controller or policy. | Return the application’s normal authorization or validation response. |
| Missing font or invalid image | Builder test and application logging. | Fail the request or job; do not emit partial PDFs. |
| Unsafe output path | Application storage service and GeneratePdfJob. | Reject before dispatch and rely on worker-side validation as defense in depth. |
| Signing or TSA failure | Signing service boundary. | Decide whether the document may be unsigned; default to fail closed for regulated documents. |
| Queue timeout | Queue worker configuration and observability. | Retry only when the builder is deterministic and output path is safe to overwrite. |
Safe defaults
Section titled “Safe defaults”| Concern | Default | When to override |
|---|---|---|
| Queue name | pdf | Use a dedicated queue when generation competes with user-facing jobs. |
| Job timeout | 120 seconds | Increase only after measuring document size and worker capacity. |
| Response filename | document.pdf | Use sanitized business identifiers. |
| Font registry | Shared and locked after warmup. | Add preload_fonts for fonts used on hot paths. |
| Image registry | Shared bounded cache. | Lower image_cache_mb for memory-constrained workers. |
| Streamed response chunking | 64 KB chunks. | Do not depend on chunk boundaries; they are an output detail. |
Testing checklist
Section titled “Testing checklist”- Controller tests assert
Content-Type,Content-Disposition, and defensive headers. - Builder tests use deterministic DTOs and do not query the database.
- Queue tests assert the builder receives a fresh document.
- Path tests cover traversal, stream-wrapper, null-byte, and non-
.pdfrejection. - Worker tests render representative documents under the same memory limit as production.
- Optional signing tests cover missing certificate, invalid password, TSA unavailable, and configured signature level.