Przejdź do głównej zawartości

Użycie produkcyjne pakietu NextPDF dla Laravela

W środowisku produkcyjnym rozwiązuj kontrakt dokumentu przez wstrzykiwanie przez konstruktor. Obsługuj niepowodzenia zapisu pliku PDF za pomocą konkretnego wyjątku. Ciężkie lub wsadowe generowanie przenieś do GeneratePdfJob i podłącz jawne wywołania zwrotne sukcesu i niepowodzenia.

Okno terminala
composer require nextpdf/laravel
php artisan vendor:publish --tag=nextpdf-config

Skonfiguruj połączenie kolejki w pliku config/nextpdf.php. Ustaw queue.connection, queue.queue oraz queue.timeout. Następnie upewnij się, że proces roboczy działa na skonfigurowanym połączeniu.

Kontener udostępnia NextPDF\Contracts\PdfDocumentInterface jako powiązanie fabryczne. Każde rozwiązanie zależności zwraca świeży obiekt NextPDF\Core\Document. PSR-11 zezwala kontenerowi na zwracanie różnych wartości z kolejnych wywołań get(), w zależności od strategii powiązania (PSR-11 §1.1.2). Ten pakiet korzysta z powiązania fabrycznego, dzięki czemu zmienny stan ograniczony do żądania nigdy nie przechodzi między żądaniami. Rejestry czcionek i obrazów są singletonami. Dzięki temu zachowany jest kontrakt, zgodnie z którym powiązany identyfikator jest rozwiązywany do zarejestrowanego wpisu (PSR-11 §1.1.2), a jednocześnie kosztowne zasoby nadal są współdzielone w obrębie procesu roboczego.

W kodzie produkcyjnym preferuj wstrzykiwanie przez konstruktor zamiast fasady. Dzięki temu zależność jest jawna, a kontroler można testować jednostkowo bez uruchamiania korzenia fasady.

Kontroler z wstrzykiwaniem zależności i typowaną obsługą błędów

Dział zatytułowany „Kontroler z wstrzykiwaniem zależności i typowaną obsługą błędów”
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);
}
}
}

Wstrzykuj PdfDocumentInterface, a nie konkretny Document, aby móc podmieniać powiązanie w testach. Kontener zwraca świeży dokument przy każdym utworzeniu instancji kontrolera. Nie używaj ponownie tej samej instancji kontrolera dla dwóch niepowiązanych dokumentów w obrębie jednego procesu.

Blok catch rejestruje klasę wyjątku i zwraca kontrolowany błąd HTTP zamiast ujawniać ślad stosu. Użyj Psr\Log\LoggerInterface, który kontener rozwiązuje do mechanizmu rejestrowania frameworka. PSR-3 pozostawia escapowanie symboli zastępczych implementatorowi i nakazuje wywołującym, aby nie escapowali wstępnie wartości kontekstu (PSR-3 §1.2). Przekazuj ustrukturyzowany kontekst, a nie interpolowane ciągi znaków.

Kolejkowane generowanie z wywołaniami zwrotnymi sukcesu i niepowodzenia

Dział zatytułowany „Kolejkowane generowanie z wywołaniami zwrotnymi sukcesu i niepowodzenia”

GeneratePdfJob to zadanie ShouldQueue. Domyślnie wykonuje trzy próby, ma limit czasu 120 sekund oraz 10-sekundowy backoff. Wszystkie trzy wartości możesz nadpisać w pliku config/nextpdf.php. Domknięcie budujące dokument otrzymuje dokument rozwiązany przez kontener i musi zwrócić skonfigurowany dokument.

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() przekazuje swoje argumenty bezpośrednio do konstruktora (string $outputPath, callable $builder, ?callable $onSuccess, ?callable $onFailure). W rezultacie wywołania zwrotne sukcesu i niepowodzenia są podłączone do tego samego zadania, które trafia do kolejki. Odpowiada to pozycyjnej formie GeneratePdfJob::dispatch($path, $builder) z /integrations/laravel/quickstart/. Wywołanie zwrotne sukcesu otrzymuje ścieżkę wyjściową, a wywołanie zwrotne niepowodzenia otrzymuje obiekt Throwable. Zadanie udostępnia również płynne settery then() oraz catch(), które zwracają zadanie i umożliwiają łączenie wywołań. Tych setterów używaj tylko wtedy, gdy zachowujesz i wysyłasz tę samą instancję, na przykład za pomocą funkcji pomocniczej dispatch(). Zadanie udostępnia także metodę failed(), którą mechanizm kolejki wywołuje przy niepowodzeniu terminalnym. Wywołania zwrotne są opakowane w domknięcia możliwe do serializacji, dzięki czemu przetrwają transport kolejki.

WłaściwośćWartość domyślnaKlucz konfiguracji
tries3nie jest sterowane konfiguracją; aby zmienić, utwórz podklasę
timeout120nextpdf.queue.timeout
backoff10nie jest sterowane konfiguracją; aby zmienić, utwórz podklasę
nazwa kolejkipdfnextpdf.queue.queue
połączeniedomyślnenextpdf.queue.connection

tries oraz backoff to właściwości publiczne odczytywane z instancji zadania. Zadanie dostarczane z pakietem nie odczytuje ich z konfiguracji. Jeśli obowiązuje inna polityka ponawiania, utwórz podklasę GeneratePdfJob, aby je nadpisać.

  • Domknięcie budujące musi zwrócić PdfDocumentInterface. Zadanie zapisuje zwróconą wartość, a nie pierwotnie rozwiązaną instancję. Test zadania jawnie weryfikuje ten kontrakt.
  • Rozwiązanie SignerInterface zwraca null, chyba że podpisywanie jest włączone, certyfikat jest skonfigurowany oraz zainstalowano nextpdf/premium. Zawsze sprawdzaj wartość null przed podpisaniem.
  • Długo działające procesy robocze (Octane/RoadRunner/Swoole) współdzielą zablokowany rejestr czcionek. Skonfiguruj preload_fonts, aby rozgrzanie nastąpiło raz przy starcie procesu roboczego, a nie przy pierwszym żądaniu.
  • Dla zadania zakończonego niepowodzeniem failed() jest wywoływane po wyczerpaniu tries. Niepowodzenie pojedynczej próby nie wywołuje onFailure, dopóki mechanizm kolejki nie zadeklaruje niepowodzenia terminalnego.

Synchroniczne generowanie w kontrolerze blokuje żądanie na czas pełnego zbudowania pliku PDF. W przypadku wielostronicowego lub wsadowego wyjścia wyślij GeneratePdfJob i zwróć odpowiedź natychmiast. Rejestry singletonowe rozkładają koszt parsowania czcionek i dekodowania obrazów na cały czas życia procesu roboczego. Koszt przypadający na żądanie ogranicza się wtedy do budowy dokumentu i emisji treści.

Kontroler ze wstrzykiwaniem zależności rejestruje klasę wyjątku, a nie jego komunikat ani ślad, aby uniknąć ujawniania wewnętrznych szczegółów w dziennikach. GeneratePdfJob weryfikuje ścieżkę wyjściową w procesie roboczym, aby ograniczyć ryzyko zmanipulowanych zserializowanych ładunków w transporcie kolejki. Pełne omówienie znajdziesz w /integrations/laravel/security-and-operations/.

TwierdzenieŹródłoKlauzulareference_id
Powiązany identyfikator jest rozwiązywany do zarejestrowanego wpisuPSR-11 Container§1.1.2
Kolejne rozwiązania mogą się różnić w zależności od strategii powiązania (powiązanie fabryczne)PSR-11 Container§1.1.2

Wskazówki dotyczące rejestrowania w PSR-3 znajdują się w specyfikacji PSR-3. Przypisują one escapowanie symboli zastępczych implementatorowi i wskazują wywołującym, aby przekazywali ustrukturyzowany kontekst. Zobacz dokument psr_3_logger §1.2.

Podpisane wyjście PAdES B-B oraz archiwizacja PDF/A przez nextpdf/premium korzystają z tej samej powierzchni wstrzykiwania zależności (DI). To opcjonalna funkcja Enterprise. Udokumentowany tutaj pakiet Core nie wymaga żadnej zmiany kodu, aby z niej skorzystać. Zobacz https://nextpdf.dev/get-license/?intent=laravel-signing.

  • /integrations/laravel/quickstart/ — minimalny pierwszy przykład
  • /integrations/laravel/configuration/ — klucze kolejki, podpisu i czcionek
  • /integrations/laravel/security-and-operations/ — model zagrożeń i utwardzanie
  • /integrations/laravel/troubleshooting/ — typowe niepowodzenia produkcyjne