İçeriğe geç

Kuyruğa alınmış işte PDF oluşturma

Ağır PDF oluşturma işlemi istek iş parçacığında çalışmamalıdır. Her framework entegrasyonu, PDF’i işçi üzerinde oluşturup kaydeden kuyruğa alınmış bir oluşturma API’si sunar. HTTP isteği, işi kuyruğa gönderir göndermez yanıt döndürebilir. Bu kılavuz, Laravel (GeneratePdfJob), Symfony (Messenger üzerinden GeneratePdfMessage) ve CodeIgniter 4 (GeneratePdfJob, codeigniter4/queue aracılığıyla) için kuyruğa alma yolunu kapsar.

Ön koşullar şunlardır:

  • NextPDF core ve bir framework entegrasyonu kuruludur.
  • Bir kuyruk taşıyıcısı yapılandırılmıştır: bir Laravel kuyruk bağlantısı, bir Symfony Messenger taşıyıcısı veya codeigniter4/queue kurulu bir CodeIgniter 4 kuyruğu.
  • O taşıyıcı için bir işçi süreci çalışmaktadır.

Bu kılavuz, uygulamanızda zaten bir kuyruk bulunduğunu varsayar. Kuyruk veya Messenger kurulumu için kendi framework’ünüzün belgelerine başvurun.

Entegrasyonu yükleyin, ardından framework’ünüzün gerektirdiği kuyruk bağımlılığını ekleyin.

Terminal window
composer require nextpdf/laravel
Terminal window
composer require nextpdf/symfony symfony/messenger

CodeIgniter’ın kuyruk paketine ihtiyacı vardır. Entegrasyon bunu yalnızca geliştirme bağımlılığı olarak bildirir; bu nedenle işçileri çalıştıran uygulamada paketi require ile ekleyin.

Terminal window
composer require nextpdf/codeigniter codeigniter4/queue

Laravel için kuyruk bağlantısını config/nextpdf.php dosyasında (queue.connection, queue.queue, queue.timeout) yapılandırın, ardından o bağlantı için bir işçi çalıştırın.

Her entegrasyon, kendi framework’ünün biçimine uygun olarak aynı deseni kullanır:

  • Laravel, NextPDF\Laravel\Jobs\GeneratePdfJob ile birlikte gelir; bu, bir ShouldQueue işidir. Bu işi bir çıktı yolu ve bir oluşturucu kapanışı ile kuyruğa gönderirsiniz. Kapanış, konteyner tarafından çözümlenen bir belgeyi alır ve yapılandırılmış belgeyi döndürür. İşçi üzerinde iş, döndürülen belgeyi belirtilen yola kaydeder. İsteğe bağlı başarı ve başarısızlık geri çağrılarını da kabul eder.
  • Symfony, NextPDF\Symfony\Message\GeneratePdfMessage ile birlikte gelir; bu, Messenger veri yolunda gönderilen readonly bir mesajdır ve yanında GeneratePdfHandler bulunur. İşleyici, bir oluşturucuyu sınıf adına göre bir PSR-11 hizmet bulucusundan çözümler. Her belge türü için NextPDF\Symfony\Message\PdfBuilderInterface arayüzünü siz uygularsınız.
  • CodeIgniter 4, NextPDF\CodeIgniter\Jobs\GeneratePdfJob ile birlikte gelir; bu, Config\Queue::$jobHandlers içinde bir ad anahtarıyla kayıtlıdır. İşi kayıtlı adıyla, bir oluşturucu referansı, bir çıktı yolu ve bir bağlam dizisi ile push edersiniz. Oluşturucu, App\PdfBuilders ad alanıyla sınırlandırılmış statik bir yöntemdir.

Üç entegrasyonun tümü aynı güvenlik duruşunu paylaşır: çıktı yolunu doğrularlar. Symfony ve CodeIgniter bunu tüketim anında yeniden doğrular; çünkü bir yük, gönderim ile yürütme arasında kuyrukta bekleyebilir. Oluşturucu, işçi üzerinde yeni bir belge üzerinde çalışır; böylece eşzamanlı işler belge durumunu hiçbir zaman paylaşmaz.

KonuLaravelSymfonyCodeIgniter 4
Kuyruğa alınan birimGeneratePdfJob (ShouldQueue)GeneratePdfMessage (DTO) + GeneratePdfHandlerGeneratePdfJob (kuyruk işleyicisi)
GönderimGeneratePdfJob::dispatch($path, $builder, $onSuccess, $onFailure)MessageBusInterface::dispatch(new GeneratePdfMessage(...))service('queue')->push($queue, $name, $data)
Oluşturucu şeklicallable(PdfDocumentInterface): PdfDocumentInterfacePdfBuilderInterface::build(Document, array): Documentstatic fn(Document, array): Document, ad alanı App\PdfBuilders
Yol / girdi korumasıİş, çıktı yolunu işçi üzerinde doğrularDTO oluşturma anında doğrular, işleyici tüketim anında yeniden doğrularİş, yolu WRITEPATH/pdfs/ ile sınırlar; oluşturucu ad alanını izin listesine alır
Başarısızlık yüzeyifailed(), tries tükendiğinde; onFailure nihai başarısızlıktaMessenger yeniden deneme stratejisi; türlenmiş doğrulama hatalarıInvalidArgumentException / QueueException

Her framework’te en yalın gönderim için bunu kullanın.

Laravel: dispatch GeneratePdfJob
<?php
declare(strict_types=1);
use NextPDF\Contracts\PdfDocumentInterface;
use NextPDF\Laravel\Jobs\GeneratePdfJob;
GeneratePdfJob::dispatch(
storage_path('app/reports/january-2026.pdf'),
static fn (PdfDocumentInterface $document): PdfDocumentInterface => $document
->addPage()
->cell(0, 10, 'January report', newLine: true),
);

Çıktı yolu .pdf ile bitmelidir; iş, dosyayı yazmadan önce yolu işçi üzerinde doğrular.

Symfony: dispatch GeneratePdfMessage from a controller
<?php
declare(strict_types=1);
namespace App\Controller;
use App\Pdf\InvoicePdfBuilder;
use NextPDF\Symfony\Message\GeneratePdfMessage;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Routing\Attribute\Route;
final class ReportController
{
#[Route('/invoice/{id}/queue', name: 'invoice_queue')]
public function queue(MessageBusInterface $bus, int $id): Response
{
$bus->dispatch(new GeneratePdfMessage(
builderClass: InvoicePdfBuilder::class,
outputPath: '/var/storage/invoices/' . $id . '.pdf',
builderContext: ['invoice_id' => $id],
));
return new Response('PDF generation queued.', 202);
}
}
CodeIgniter 4: push GeneratePdfJob by its registered name
<?php
declare(strict_types=1);
namespace App\Controllers;
use CodeIgniter\HTTP\ResponseInterface;
final class InvoiceController extends BaseController
{
public function queueInvoice(int $id): ResponseInterface
{
service('queue')->push('pdf-queue', 'generate-pdf', [
'builder' => 'App\\PdfBuilders\\InvoiceBuilder::build',
'outputPath' => WRITEPATH . 'pdfs/invoice-' . $id . '.pdf',
'context' => ['invoice_id' => $id],
]);
return $this->response
->setStatusCode(ResponseInterface::HTTP_ACCEPTED)
->setJSON(['status' => 'queued', 'invoice_id' => $id]);
}
}

CodeIgniter’da iş sınıfı dizesini değil, jobHandlers anahtarını ('generate-pdf') push edin. İşleyiciyi önce app/Config/Queue.php dosyasında kaydedin.

CodeIgniter 4: app/Config/Queue.php
<?php
declare(strict_types=1);
namespace Config;
use CodeIgniter\Queue\Config\Queue as BaseQueue;
use NextPDF\CodeIgniter\Jobs\GeneratePdfJob;
final class Queue extends BaseQueue
{
/** @var array<string, class-string> */
public array $jobHandlers = [
'generate-pdf' => GeneratePdfJob::class,
];
}

Üretimde yapılan bir gönderim, başarı ve başarısızlık geri çağrılarını (Laravel) ya da açıkça kaydedilmiş bir oluşturucuyu ve türlenmiş bir işleyiciyi (Symfony) bir PSR-3 günlükleyicisine bağlar. Aşağıdaki Laravel örneği her iki geri çağrıyla birlikte gönderim yapar.

Laravel: app/Jobs/DispatchMonthlyStatement.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
{
// dispatch() is public static: it constructs the job from the
// arguments it receives. Pass every argument — including the
// callbacks — to the static call, not to a separately built instance.
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,
]);
},
);
}
}

Başarı geri çağrısı çıktı yolunu alır. Başarısızlık geri çağrısı Throwable nesnesini alır. İş, başarısızlık akışı çalışmadan önce tries değerini (varsayılan 3) tüketir. timeout değerini nextpdf.queue.timeout aracılığıyla ayarlayın. tries ve backoff değerleri public özelliklerdir; bu nedenle onları değiştirmek için GeneratePdfJob sınıfını alt sınıflayın.

Symfony için oluşturucuyu uygulayın ve onu bir hizmet bulucusuna kaydedin. Bu, işleyiciyi kayıtlı oluşturucularla sınırlı tutar.

Symfony: src/Pdf/InvoicePdfBuilder.php
<?php
declare(strict_types=1);
namespace App\Pdf;
use NextPDF\Core\Document;
use NextPDF\Symfony\Message\PdfBuilderInterface;
final class InvoicePdfBuilder implements PdfBuilderInterface
{
/** @param array<string, mixed> $context */
public function build(Document $document, array $context): Document
{
$document->addPage();
$document->setFont('dejavusans', '', 12);
$document->cell(0, 10, 'Invoice #' . $context['invoice_id']);
return $document;
}
}
Symfony: config/services.yaml (builder locator)
services:
App\Pdf\InvoicePdfBuilder: ~
nextpdf.pdf_builder_locator:
class: Symfony\Component\DependencyInjection\ServiceLocator
arguments:
- 'App\Pdf\InvoicePdfBuilder': '@App\Pdf\InvoicePdfBuilder'
tags: ['container.service_locator']
NextPDF\Symfony\Message\GeneratePdfHandler:
arguments:
$builderLocator: '@nextpdf.pdf_builder_locator'

CodeIgniter için oluşturucuyu App\PdfBuilders altında statik bir yöntem olarak uygulayın. İş, bu ad alanının dışındaki herhangi bir oluşturucu referansını ve WRITEPATH/pdfs/ dışındaki herhangi bir çıktı yolunu reddeder.

CodeIgniter 4: app/PdfBuilders/InvoiceBuilder.php
<?php
declare(strict_types=1);
namespace App\PdfBuilders;
use NextPDF\Core\Document;
final class InvoiceBuilder
{
/** @param array<string, mixed> $context */
public static function build(Document $document, array $context): Document
{
$invoiceId = (int) ($context['invoice_id'] ?? 0);
$document->addPage();
$document->cell(0, 10, "Invoice #{$invoiceId}");
return $document;
}
}

Her framework için işçiyi çalıştırın.

Terminal window
php bin/console messenger:consume async --limit=200 --memory-limit=256M --time-limit=3600
Terminal window
php spark queue:work pdf-queue

Bir bağımlılıkta sızan bellek tahsisinin sınırsız büyümesini önlemek için Laravel ve Symfony işçilerini sınırlı yaşam süreleriyle (--limit / --memory-limit / --time-limit) geri dönüştürün.

  • Kaydedilen şey, oluşturucunun döndürdüğü değerdir. Her entegrasyonda işçi, başlangıçta çözümlediği örneği değil, oluşturucunun döndürdüğü belgeyi kaydeder. Yapılandırılmış belgeyi oluşturucudan her zaman döndürün.
  • Yol doğrulaması işçi üzerinde çalışır. Symfony, çıktı yolunu oluşturma anında ve tüketim sırasında yeniden doğrular. CodeIgniter, yolu WRITEPATH/pdfs/ ile sınırlar ve dizin geçişi ile kardeş-önek yollarını reddeder. Gönderim anında güvenli ama tüketim anında güvenli olmayan bir yol yine de reddedilir.
  • CodeIgniter’da sınıfı değil adı push edin. İş adı olarak GeneratePdfJob::class değerini push ederseniz, kuyruk bunu push anında reddeder. Bunun yerine jobHandlers anahtarını push edin.
  • Laravel geri çağrıları statik gönderime aktarılmalıdır. Bir iş örneği oluşturup ardından $job->dispatch(...) çağrısını yaparsanız, o çağrı örneği ve onun geri çağrılarını yok sayar. Geri çağrıları GeneratePdfJob::dispatch(...) çağrısına aktarın.
  • İşçi açısından güvenli kayıt defterleri. Yazı tipi kayıt defteri, süreç yaşam süresi boyunca kilitli bir tekildir ve görüntü kayıt defteri, sınırlı bir önbellektir. Belgeler, her iş için yenidir. İşçi üzerinde paylaşılan bir belge varsaymayın.
  • İşçilerde imzalama. Bir kuyruk işinde imzalı ya da PDF/A çıktısı, işçi ortamında kurulu ticari bir NextPDF sürümünü gerektirir. Bu olmadan, imzalama hizmeti null değerine çözümlenir. İmzalamadan önce null denetimi yapın.

PDF oluşturmayı kuyruğa alınmış bir işe taşımak, toplam PDF oluşturma süresini HTTP isteğinin dışına çıkarır. İş gönderildikten sonra istek yanıt döndürür. Yazı tipi ve görüntü kayıt defterleri kurulum maliyetlerini işçi yaşam süresine yayar; böylece iş başına maliyet, belge oluşturma ve içerik yayımı ile sınırlı kalır. Devam eden iş sayısını işçi havuzunuza göre boyutlandırın ve yazı tipi ısınmasının ilk işte değil işçi başlatıldığında bir kez gerçekleşmesi için preload_fonts değerini (Laravel, Symfony) önceden doldurun.

  • Aracı erişilebilirse kuyruk yüklerinin saldırgan etkisi altında olabileceğini varsayın; bu nedenle bir yükteki çıktı yolunu ve oluşturucu referansını güvenilmeyen olarak ele alın. Entegrasyonlar bunu, yol doğrulaması ve CodeIgniter’da bir oluşturucu ad alanı izin listesi ile uygular.
  • Derinlemesine savunma olarak işçi dosya sistemi izinlerini hedeflenen çıktı dizini ile kısıtlayın. Kurcalanmış bir yol bir biçimde doğrulamayı geçse bile, yine de dizinden kaçamaz.
  • Başarısızlık geri çağrısında istisna sınıfını ve bir ilişkilendirme tanımlayıcısını günlüğe yazın; mesajı ya da izlemeyi asla günlüğe yazmayın.
  • Asla boş bir catch bloğu yazmayın. Buradaki her başarısızlık geri çağrısı günlüğe yazar ve bağlam içerir.

Her entegrasyonun güvenlik ve operasyonlar sayfası, eksiksiz kuyruk tehdit modelini kapsar: yük doğrulaması, çağrılabilir izin listeleri ve yol sınırlandırması.

Bu kılavuz, normatif hiçbir standart iddiası yapmaz. Gösterilen her API çağrısı, adı geçen entegrasyonun doğrulanmış genel yüzeyidir. Kuyruğa alınmış yol, konteyner bağlama garantilerine dayanır: çözümleme başına yeni bir belge ve kilitli yazı tipi kayıt defteri. Aşağıdaki Ayrıca bakın bölümünde bağlantısı verilen yukarı akış üretim kullanımı sayfaları, bu garantileri PSR alıntılarıyla belgeler. Bu Cookbook sayfası kullanımı yeniden açıklar ve alıntıları o sayfalara bırakır.