Skip to content

佇列任務

GeneratePdfJob 提供開箱即用的佇列式 PDF 產生功能。對於耗時的文件,將工作交給背景工作者可以避免阻塞 HTTP 請求。

php
use Yeeefang\TcpdfNext\Laravel\Jobs\GeneratePdfJob;

派發任務

建立任務時傳入一個閉包,該閉包接收 Document 實例並定義 PDF 的內容:

php
use Yeeefang\TcpdfNext\Laravel\Jobs\GeneratePdfJob;

GeneratePdfJob::dispatch(
    builder: function ($pdf) use ($report) {
        $pdf->setTitle($report->title)
            ->addPage()
            ->setFont('Helvetica', 'B', 16)
            ->cell(0, 10, $report->title)
            ->setFont('Helvetica', '', 11)
            ->multiCell(0, 6, $report->body);
    },
    outputPath: storage_path("app/reports/{$report->id}.pdf"),
);

成功與失敗回呼

透過 onSuccessonFailure 回呼處理任務完成後的邏輯:

php
GeneratePdfJob::dispatch(
    builder: function ($pdf) use ($invoice) {
        $pdf->setTitle("發票 #{$invoice->number}")
            ->addPage()
            ->setFont('Helvetica', '', 12)
            ->cell(0, 10, "發票 #{$invoice->number}");
    },
    outputPath: storage_path("app/invoices/{$invoice->id}.pdf"),
    onSuccess: function (string $path) use ($invoice) {
        $invoice->update(['pdf_path' => $path, 'status' => 'ready']);
        Mail::to($invoice->customer)->send(new InvoiceReady($invoice));
    },
    onFailure: function (\Throwable $e) use ($invoice) {
        $invoice->update(['status' => 'generation_failed']);
        Log::error('PDF 產生失敗', [
            'invoice' => $invoice->id,
            'error' => $e->getMessage(),
        ]);
    },
);

佇列設定

指定佇列名稱、連線與延遲:

php
GeneratePdfJob::dispatch(
    builder: $builder,
    outputPath: $path,
)->onQueue('pdf-generation')
 ->onConnection('redis')
 ->delay(now()->addMinutes(5));

重試與逾時

config/tcpdf-next.php 中設定預設值,或在派發時覆寫:

php
// config/tcpdf-next.php
'queue' => [
    'connection' => env('TCPDF_QUEUE_CONNECTION', 'redis'),
    'name'       => env('TCPDF_QUEUE_NAME', 'pdf-generation'),
    'tries'      => 3,
    'timeout'    => 120,
    'backoff'    => [10, 30, 60],
],

任務會使用指數退避策略:第一次重試等待 10 秒、第二次 30 秒、第三次 60 秒。

批次處理

使用 Laravel 的 Bus::batch() 同時產生多份 PDF:

php
use Illuminate\Support\Facades\Bus;
use Yeeefang\TcpdfNext\Laravel\Jobs\GeneratePdfJob;

$jobs = $invoices->map(fn (Invoice $invoice) => new GeneratePdfJob(
    builder: function ($pdf) use ($invoice) {
        $pdf->setTitle("發票 #{$invoice->number}")
            ->addPage()
            ->setFont('Helvetica', '', 12)
            ->cell(0, 10, "發票 #{$invoice->number}");
    },
    outputPath: storage_path("app/invoices/{$invoice->id}.pdf"),
));

Bus::batch($jobs)
    ->name('批次發票產生')
    ->onQueue('pdf-generation')
    ->allowFailures()
    ->then(function () {
        Log::info('所有發票 PDF 已產生完畢');
    })
    ->catch(function (\Throwable $e) {
        Log::error('部分發票產生失敗', ['error' => $e->getMessage()]);
    })
    ->finally(function () {
        // 無論成敗皆執行
        Notification::send($admins, new BatchCompleteNotification());
    })
    ->dispatch();

搭配 Blade 視圖

結合 Blade 模板產生更複雜的 PDF:

php
GeneratePdfJob::dispatch(
    builder: function ($pdf) use ($order) {
        $html = view('pdf.order-confirmation', [
            'order' => $order,
            'items' => $order->items,
        ])->render();

        $pdf->setTitle("訂單確認 #{$order->id}")
            ->writeHtml($html);
    },
    outputPath: storage_path("app/orders/{$order->id}.pdf"),
);

監控任務

使用 Laravel Horizon 或內建的 queue:monitor 指令追蹤 PDF 佇列狀態:

bash
php artisan queue:monitor pdf-generation --max=100

下一步

以 LGPL-3.0-or-later 授權釋出。