Lewati ke konten

Penggunaan NextPDF Laravel di produksi

Di lingkungan produksi, selesaikan kontrak dokumen melalui injeksi konstruktor. Tangani kegagalan penulisan PDF dengan eksepsi yang spesifik. Pindahkan proses pembuatan yang berat atau berbentuk batch ke GeneratePdfJob, lalu sambungkan callback keberhasilan dan kegagalan secara eksplisit.

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

Konfigurasikan koneksi antrean di config/nextpdf.php. Atur queue.connection, queue.queue, dan queue.timeout. Setelah itu, pastikan ada worker yang berjalan pada koneksi yang telah dikonfigurasi.

Container mengekspos NextPDF\Contracts\PdfDocumentInterface sebagai factory binding. Setiap proses penyelesaian menghasilkan NextPDF\Core\Document baru. PSR-11 mengizinkan container mengembalikan nilai yang berbeda dari panggilan get() yang berurutan, bergantung pada strategi binding (PSR-11 §1.1.2). Paket ini menggunakan factory binding sehingga state mutable yang cakupannya per-permintaan tidak pernah terbawa antarpermintaan. Registry fon dan gambar bersifat singleton. Hal ini menjaga kontrak bahwa identifier yang terikat diselesaikan menjadi entri terdaftarnya (PSR-11 §1.1.2), sekaligus tetap membagikan sumber daya yang mahal ini ke seluruh worker.

Utamakan injeksi konstruktor daripada facade dalam kode produksi. Cara ini membuat dependensi eksplisit dan menjaga controller tetap dapat diuji secara unit tanpa mem-boot facade root.

Controller berbasis DI dengan penanganan kesalahan bertipe

Bagian berjudul “Controller berbasis DI dengan penanganan kesalahan bertipe”
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);
}
}
}

Injeksikan PdfDocumentInterface, bukan Document konkret, agar Anda dapat menukar binding-nya dalam pengujian. Container mengembalikan dokumen baru untuk setiap instansiasi controller. Jangan gunakan ulang instance controller yang sama untuk dua dokumen yang tidak berkaitan dalam satu proses.

Blok catch mencatat kelas eksepsi dan mengembalikan kesalahan HTTP yang terdefinisi, alih-alih membocorkan stack trace. Gunakan Psr\Log\LoggerInterface, yang diselesaikan oleh container menjadi logger framework. PSR-3 menyerahkan escaping placeholder kepada pihak yang mengimplementasikan dan mengarahkan pemanggil agar tidak meng-escape nilai konteks terlebih dahulu (PSR-3 §1.2). Berikan konteks terstruktur, bukan string yang diinterpolasi.

Pembuatan terantre dengan callback keberhasilan dan kegagalan

Bagian berjudul “Pembuatan terantre dengan callback keberhasilan dan kegagalan”

GeneratePdfJob adalah job ShouldQueue. Secara standar, job ini menggunakan tiga percobaan, batas waktu 120 detik, dan backoff 10 detik. Anda dapat menimpa ketiganya di config/nextpdf.php. Closure builder menerima dokumen yang diselesaikan oleh container dan harus mengembalikan dokumen yang telah dikonfigurasi.

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() meneruskan argumennya langsung ke konstruktor (string $outputPath, callable $builder, ?callable $onSuccess, ?callable $onFailure). Akibatnya, callback keberhasilan dan kegagalan tersambung ke job yang sama dengan job yang diantrekan. Ini sesuai dengan bentuk posisional GeneratePdfJob::dispatch($path, $builder) di /integrations/laravel/quickstart/. Callback keberhasilan menerima path keluaran, dan callback kegagalan menerima Throwable. Job ini juga mengekspos setter fluent then() dan catch() yang mengembalikan job untuk dirantai. Gunakan setter tersebut hanya jika Anda menyimpan dan men-dispatch instance yang sama, misalnya melalui helper dispatch(). Job ini juga mengekspos metode failed(), yang dipanggil oleh queue runner saat terjadi kegagalan terminal. Callback dibungkus dalam closure yang dapat diserialisasi sehingga tetap bertahan melewati transport antrean.

PropertiStandarKunci konfigurasi
tries3tidak digerakkan konfigurasi; subkelaskan untuk mengubah
timeout120nextpdf.queue.timeout
backoff10tidak digerakkan konfigurasi; subkelaskan untuk mengubah
nama antreanpdfnextpdf.queue.queue
koneksidefaultnextpdf.queue.connection

tries dan backoff adalah properti publik yang dibaca dari instance job. Job yang dikirimkan tidak membacanya dari konfigurasi. Jika kebijakan percobaan ulang Anda berbeda, buat subclass dari GeneratePdfJob untuk menimpanya.

  • Closure builder harus mengembalikan PdfDocumentInterface. Job menyimpan nilai kembalian tersebut, bukan instance yang awalnya diselesaikan. Pengujian job menegaskan kontrak ini secara eksplisit.
  • Menyelesaikan SignerInterface mengembalikan null kecuali penandatanganan diaktifkan dan sertifikat dikonfigurasi serta nextpdf/premium terpasang. Selalu lakukan pemeriksaan null sebelum menandatangani.
  • Worker berumur panjang (Octane/RoadRunner/Swoole) berbagi registry fon yang terkunci. Konfigurasikan preload_fonts agar pemanasan berjalan sekali saat boot worker, bukan pada permintaan pertama.
  • Job yang gagal memanggil failed() setelah menghabiskan tries. Kegagalan per percobaan tidak memanggil onFailure sampai queue runner menyatakan kegagalan terminal.

Pembuatan sinkron di controller memblokir permintaan selama keseluruhan proses build PDF. Untuk keluaran multi-halaman atau batch, dispatch GeneratePdfJob dan segera kembalikan respons. Registry singleton menyebarkan biaya penguraian fon dan decoding gambar di sepanjang umur worker. Biaya per permintaan kemudian terbatas pada konstruksi dokumen dan emisi konten.

Controller berbasis injeksi dependensi mencatat kelas eksepsi, bukan pesan atau trace-nya, untuk menghindari kebocoran detail internal ke dalam log. GeneratePdfJob memvalidasi path keluaran pada worker untuk meredam payload terserialisasi yang dirusak pada transport antrean. Cakupan lengkap ada di /integrations/laravel/security-and-operations/.

KlaimSumberKlausulreference_id
Identifier yang terikat diselesaikan menjadi entri terdaftarnyaPSR-11 Container§1.1.2
Penyelesaian yang berurutan dapat berbeda menurut strategi binding (factory binding)PSR-11 Container§1.1.2

Panduan logging PSR-3 tercantum dalam spesifikasi PSR-3. Panduan tersebut menyerahkan escaping placeholder kepada pihak yang mengimplementasikan dan mengarahkan pemanggil untuk memberikan konteks terstruktur. Lihat dokumen psr_3_logger §1.2.

Keluaran PAdES B-B yang ditandatangani dan pengarsipan PDF/A melalui nextpdf/premium menggunakan permukaan injeksi dependensi (DI) yang sama. Ini adalah kapabilitas Enterprise opsional. Paket Core yang didokumentasikan di sini tidak memerlukan perubahan kode untuk mengadopsinya. Lihat https://nextpdf.dev/get-license/?intent=laravel-signing.

  • /integrations/laravel/quickstart/ — contoh awal yang minimal
  • /integrations/laravel/configuration/ — kunci antrean, tanda tangan, dan fon
  • /integrations/laravel/security-and-operations/ — model ancaman dan pengerasan
  • /integrations/laravel/troubleshooting/ — kegagalan produksi yang umum terjadi