Queue Job¶
GeneratePdfJob 將 PDF 生成任務推入 Laravel Queue 系統,讓耗時的 PDF 生成不阻塞 HTTP 請求週期。Job 完成後可透過 Laravel Notification 或自訂 Callback 通知使用者。
PHP Compatibility
This example uses PHP 8.5 syntax. If your environment runs PHP 8.1 or 7.4, use NextPDF Backport for a backward-compatible build.
基本用法¶
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Jobs\GenerateInvoicePdfJob;
use Illuminate\Http\JsonResponse;
final class InvoiceController extends Controller
{
public function generate(int $invoiceId): JsonResponse
{
GenerateInvoicePdfJob::dispatch($invoiceId)
->onQueue('pdf-generation')
->delay(now()->addSeconds(5));
return response()->json(['status' => 'queued']);
}
}
建立自訂 PDF Job¶
繼承 GeneratePdfJob 抽象基礎類別以實作業務邏輯:
<?php
declare(strict_types=1);
namespace App\Jobs;
use NextPDF\Laravel\Jobs\GeneratePdfJob;
use NextPDF\Core\Contracts\DocumentFactoryInterface;
final class GenerateInvoicePdfJob extends GeneratePdfJob
{
public function __construct(
private readonly int $invoiceId,
) {
parent::__construct();
}
protected function build(DocumentFactoryInterface $factory): string
{
$invoice = Invoice::findOrFail($this->invoiceId);
$document = $factory->create(title: "Invoice #{$this->invoiceId}");
$document->addPage();
$document->text($invoice->customerName, x: 20, y: 30);
return $document->output(); // 回傳 PDF bytes
}
protected function onSuccess(string $pdfBytes): void
{
// 儲存到 Storage 並通知使用者
$path = "invoices/{$this->invoiceId}.pdf";
\Storage::disk('s3')->put($path, $pdfBytes);
Invoice::find($this->invoiceId)?->user->notify(
new InvoicePdfReadyNotification(downloadUrl: \Storage::temporaryUrl($path, now()->addHour())),
);
}
}
重試與失敗處理¶
final class GenerateInvoicePdfJob extends GeneratePdfJob
{
// Job 最多重試次數
public int $tries = 3;
// 最大執行時間(秒)
public int $timeout = 120;
// 重試間隔(秒)
public array $backoff = [10, 30, 60];
// 唯一鎖:防止同一份 Invoice 同時被多個 Worker 處理
public function uniqueId(): string
{
return "invoice-pdf-{$this->invoiceId}";
}
public function failed(\Throwable $exception): void
{
\Log::error("PDF generation failed for invoice {$this->invoiceId}", [
'error' => $exception->getMessage(),
]);
Invoice::find($this->invoiceId)?->user->notify(
new InvoicePdfFailedNotification(),
);
}
}
進度追蹤¶
對於大型批次 PDF 生成,可啟用進度回報:
final class GenerateBatchReportJob extends GeneratePdfJob
{
use \Illuminate\Bus\Batchable;
protected function build(DocumentFactoryInterface $factory): string
{
$this->batch()?->add([]);
// 分頁生成,每頁後回報進度
$document = $factory->create();
foreach ($this->pageData as $index => $data) {
$document->addPage();
$document->text($data['content'], x: 20, y: 30);
// 回報進度(0–100)
$this->reportProgress(
processed: $index + 1,
total: count($this->pageData),
);
}
return $document->output();
}
}
Laravel Horizon 整合¶
// config/horizon.php — 為 PDF 生成設定專屬 Worker pool
'environments' => [
'production' => [
'pdf-supervisor' => [
'connection' => 'redis',
'queue' => ['pdf-generation'],
'balance' => 'auto',
'minProcesses' => 1,
'maxProcesses' => 4,
'memory' => 512, // PDF 生成記憶體需求較高
'timeout' => 120,
],
],
],
測試 Queue Job¶
use Illuminate\Support\Facades\Queue;
use App\Jobs\GenerateInvoicePdfJob;
it('dispatches pdf generation job', function () {
Queue::fake();
$this->post('/invoices/1/generate');
Queue::assertPushed(GenerateInvoicePdfJob::class, fn ($job) => $job->invoiceId === 1);
Queue::assertPushedOn('pdf-generation', GenerateInvoicePdfJob::class);
});
it('executes pdf job successfully', function () {
$job = new GenerateInvoicePdfJob(invoiceId: 1);
$job->handle(app(DocumentFactoryInterface::class));
Storage::assertExists('invoices/1.pdf');
});
參見¶
- Laravel 套件總覽 — 套件功能概覽
- Facade — 同步生成場景
- HTTP 回應 — 生成後直接回傳 PDF
- Service Provider — DocumentFactory DI 設定