Zum Inhalt springen

NextPDF-Laravel-Paket im produktiven Einsatz

Lösen Sie in der Produktion den Dokumentvertrag per Constructor Injection auf. Behandeln Sie PDF-Schreibfehler mit einer spezifischen Exception. Verlagern Sie aufwendige Generierung oder Batch-Generierung nach GeneratePdfJob und verdrahten Sie explizite Erfolgs- und Fehler-Callbacks.

Terminal-Fenster
composer require nextpdf/laravel
php artisan vendor:publish --tag=nextpdf-config

Konfigurieren Sie die Queue-Verbindung in config/nextpdf.php. Setzen Sie queue.connection, queue.queue und queue.timeout. Stellen Sie anschließend sicher, dass für die konfigurierte Verbindung ein Worker läuft.

Der Container stellt NextPDF\Contracts\PdfDocumentInterface über ein Factory-Binding bereit. Jede Auflösung liefert ein frisches NextPDF\Core\Document. PSR-11 erlaubt es einem Container, bei aufeinanderfolgenden get()-Aufrufen je nach Binding-Strategie unterschiedliche Werte zurückzugeben (PSR-11 §1.1.2). Das Paket verwendet dafür ein Factory-Binding, damit Request-bezogener veränderlicher Zustand niemals über Requests hinweg übertragen wird. Die Font- und Image-Registrys sind Singletons. So bleibt der Vertrag gewahrt, dass ein gebundener Bezeichner zu seinem registrierten Eintrag aufgelöst wird (PSR-11 §1.1.2), während die teuren Ressourcen weiterhin über den Worker hinweg geteilt werden.

Bevorzugen Sie im Produktionscode Constructor Injection gegenüber der Facade. Dadurch wird die Abhängigkeit explizit, und der Controller bleibt im Unit-Test prüfbar, ohne den Facade-Root zu booten.

Per DI verdrahteter Controller mit typisierter Fehlerbehandlung

Abschnitt betitelt „Per DI verdrahteter Controller mit typisierter Fehlerbehandlung“
resource: NextPDF\Contracts\PdfDocumentInterface + src/Laravel/Http/PdfResponse.php
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use Illuminate\Http\Response;
use NextPDF\Contracts\PdfDocumentInterface;
use NextPDF\Laravel\Http\PdfResponse;
use Psr\Log\LoggerInterface;
use Throwable;
final class InvoiceController extends Controller
{
public function __construct(
private readonly PdfDocumentInterface $document,
private readonly LoggerInterface $logger,
) {}
public function show(int $invoiceId): Response
{
try {
$this->document->addPage();
$this->document->cell(0, 10, "Invoice #{$invoiceId}", newLine: true);
$this->document->cell(0, 10, 'Thank you for your business.');
return PdfResponse::download(
$this->document,
"invoice-{$invoiceId}.pdf",
);
} catch (Throwable $exception) {
// Rethrow as an HTTP-meaningful failure; never swallow.
$this->logger->error('Invoice PDF generation failed', [
'invoice_id' => $invoiceId,
'exception' => $exception::class,
]);
return new Response('Could not generate the invoice PDF.', 500);
}
}
}

Injizieren Sie PdfDocumentInterface, nicht das konkrete Document, damit das Binding in Tests austauschbar bleibt. Der Container gibt pro Controller-Instanziierung ein frisches Dokument zurück. Verwenden Sie dieselbe Controller-Instanz nicht für zwei voneinander unabhängige Dokumente innerhalb eines Prozesses.

Der Catch-Block protokolliert die Exception-Klasse und gibt einen definierten HTTP-Fehler zurück, anstatt einen Stacktrace nach außen dringen zu lassen. Verwenden Sie Psr\Log\LoggerInterface, das der Container zum Framework-Logger auflöst. PSR-3 überlässt das Escaping von Platzhaltern dem Implementierer und weist Aufrufer an, Kontextwerte nicht vorab zu escapen (PSR-3 §1.2). Übergeben Sie strukturierten Kontext, keine interpolierten Strings.

Generierung in der Queue mit Erfolgs- und Fehler-Callbacks

Abschnitt betitelt „Generierung in der Queue mit Erfolgs- und Fehler-Callbacks“

GeneratePdfJob ist ein ShouldQueue-Job. Standardmäßig verwendet er drei Versuche, ein Timeout von 120 Sekunden und ein Backoff von 10 Sekunden. Alle drei können Sie über config/nextpdf.php überschreiben. Die Builder-Closure erhält das vom Container aufgelöste Dokument und muss ein konfiguriertes Dokument zurückgeben.

resource: src/Laravel/Jobs/GeneratePdfJob.php
<?php
declare(strict_types=1);
namespace App\Jobs;
use NextPDF\Contracts\PdfDocumentInterface;
use NextPDF\Laravel\Jobs\GeneratePdfJob;
use Psr\Log\LoggerInterface;
use Throwable;
final class DispatchMonthlyStatement
{
public function __construct(private readonly LoggerInterface $logger) {}
public function __invoke(int $accountId): void
{
// Dispatchable::dispatch() is `public static`: it constructs the
// job from the arguments it receives and returns a PendingDispatch.
// Pass every constructor argument — including the callbacks — to
// the static call. Building an instance and then calling
// `$job->dispatch(...)` would discard that instance (and its
// callbacks) and queue a different job from only the static args.
GeneratePdfJob::dispatch(
storage_path("app/statements/{$accountId}.pdf"),
static fn (PdfDocumentInterface $document): PdfDocumentInterface => $document
->addPage()
->cell(0, 10, "Statement for account {$accountId}", newLine: true),
function (string $path) use ($accountId): void {
$this->logger->info('Statement PDF written', [
'account_id' => $accountId,
'path' => $path,
]);
},
function (Throwable $exception) use ($accountId): void {
$this->logger->error('Statement PDF failed', [
'account_id' => $accountId,
'exception' => $exception::class,
]);
},
);
}
}

GeneratePdfJob::dispatch() leitet seine Argumente direkt an den Constructor (string $outputPath, callable $builder, ?callable $onSuccess, ?callable $onFailure) weiter. Damit werden die Erfolgs- und Fehler-Callbacks in genau den Job verdrahtet, der in die Queue gestellt wird. Das entspricht der positionsbasierten Form GeneratePdfJob::dispatch($path, $builder) in /integrations/laravel/quickstart/. Der Erfolgs-Callback erhält den Ausgabepfad und der Fehler-Callback das Throwable. Der Job stellt außerdem fluente then()- und catch()-Setter bereit, die den Job zur Verkettung zurückgeben. Verwenden Sie diese Setter nur, wenn Sie dieselbe Instanz behalten und dispatchen, etwa über den Helper dispatch(). Der Job stellt zudem eine failed()-Methode bereit, die der Queue-Runner bei endgültigem Fehlschlag aufruft. Die Callbacks werden in serialisierbare Closures verpackt, damit sie über den Queue-Transport hinweg erhalten bleiben.

PropertyStandardConfig-Schlüssel
tries3nicht config-gesteuert; zum Ändern eine Subklasse bilden
timeout120nextpdf.queue.timeout
backoff10nicht config-gesteuert; zum Ändern eine Subklasse bilden
Queue-Namepdfnextpdf.queue.queue
Verbindungdefaultnextpdf.queue.connection

tries und backoff sind öffentliche Properties, die aus der Job-Instanz gelesen werden. Der ausgelieferte Job liest sie nicht aus der Config. Wenn Ihre Retry-Policy abweicht, bilden Sie zum Überschreiben eine Subklasse von GeneratePdfJob.

  • Die Builder-Closure muss ein PdfDocumentInterface zurückgeben. Der Job speichert diesen Rückgabewert, nicht die ursprünglich aufgelöste Instanz. Der Job-Test prüft diesen Vertrag explizit.
  • Die Auflösung von SignerInterface ergibt null, solange Signieren nicht aktiviert, kein Zertifikat konfiguriert oder nextpdf/premium nicht installiert ist. Prüfen Sie vor dem Signieren immer auf null.
  • Langlebige Worker (Octane/RoadRunner/Swoole) teilen sich die gesperrte Font-Registry. Konfigurieren Sie preload_fonts, damit das Warmup einmalig beim Worker-Start statt beim ersten Request passiert.
  • Bei einem fehlgeschlagenen Job wird failed() aufgerufen, nachdem tries aufgebraucht ist. Der Fehlschlag eines einzelnen Versuchs löst onFailure erst dann aus, wenn der Queue-Runner den endgültigen Fehlschlag feststellt.

Synchrone Generierung im Controller blockiert den Request während des gesamten PDF-Builds. Für mehrseitige Ausgabe oder Batch-Ausgabe dispatchen Sie GeneratePdfJob und kehren sofort zurück. Die Singleton-Registrys verteilen Font-Parsing und Image-Decoding über die Lebensdauer des Workers hinweg. Die Kosten pro Request beschränken sich dann auf Dokumentaufbau und Inhaltsausgabe.

Der DI-Controller protokolliert die Exception-Klasse, nicht ihre Nachricht oder ihren Trace, um interne Details nicht in Logs offenzulegen. GeneratePdfJob validiert den Ausgabepfad auf dem Worker, um manipulierte serialisierte Payloads auf dem Queue-Transport zu entschärfen. Die vollständige Abdeckung finden Sie unter /integrations/laravel/security-and-operations/.

AussageQuelleKlauselreference_id
Gebundener Bezeichner wird zu seinem registrierten Eintrag aufgelöstPSR-11 Container§1.1.2
Aufeinanderfolgende Auflösungen können sich je nach Binding-Strategie unterscheiden (Factory-Binding)PSR-11 Container§1.1.2

Die Hinweise zum PSR-3-Logging sind in der PSR-3-Spezifikation dokumentiert. Sie besagen, dass das Escaping von Platzhaltern in der Verantwortung des Implementierers liegt und dass Aufrufer strukturierten Kontext übergeben. Siehe Doc psr_3_logger §1.2.

Signierte PAdES B-B-Ausgabe und PDF/A-Archivierung über nextpdf/premium nutzen dieselbe DI-Oberfläche. Das ist eine optionale Enterprise-Fähigkeit. Für die Übernahme sind im hier dokumentierten Core-Paket keine Codeänderungen erforderlich. Siehe https://nextpdf.dev/get-license/?intent=laravel-signing.

  • /integrations/laravel/quickstart/ — minimales erstes Beispiel
  • /integrations/laravel/configuration/ — Queue-, Signatur- und Font-Schlüssel
  • /integrations/laravel/security-and-operations/ — Bedrohungsmodell und Härtung
  • /integrations/laravel/troubleshooting/ — häufige Produktionsfehler