跳轉到

Queue Jobs

GeneratePdfJob 將 PDF 生成任務排入背景處理,避免阻塞 HTTP 請求週期。套件提供與 CodeIgniter Tasks(codeigniter4/tasks)及主流第三方佇列套件(如 codeigniter4-queue)的整合介面。

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.

基本架構

HTTP 請求
    └─▶ 推送任務至佇列 (Redis/Database)
              └─▶ Worker 程序
                        └─▶ GeneratePdfJob::execute()
                                  ├─▶ pdf_document() 生成 PDF
                                  ├─▶ 儲存至 writable/ 或雲端
                                  └─▶ 觸發完成回調

建立自訂 PDF Job

繼承 GeneratePdfJob 抽象類別:

<?php

declare(strict_types=1);

namespace App\Jobs;

use NextPDF\CodeIgniter\Jobs\GeneratePdfJob;
use NextPDF\Core\Document;

final class GenerateInvoicePdfJob extends GeneratePdfJob
{
    public function __construct(
        private readonly int $invoiceId,
    ) {}

    /**
     * 建構 PDF 文件。
     */
    protected function buildDocument(Document $document): void
    {
        $invoice = model('InvoiceModel')->find($this->invoiceId);

        $document->addPage();
        $document->text("Invoice #{$invoice->id}", x: 20, y: 30, fontSize: 20);
        $document->text("Customer: {$invoice->customer_name}", x: 20, y: 50);
        $document->text("Amount: \${$invoice->total}", x: 20, y: 70);
    }

    /**
     * PDF 生成成功後的回調。
     *
     * @param non-empty-string $pdfBytes
     */
    protected function onSuccess(string $pdfBytes): void
    {
        $path = WRITEPATH . "uploads/invoices/invoice-{$this->invoiceId}.pdf";
        file_put_contents($path, $pdfBytes);

        // 更新資料庫紀錄
        model('InvoiceModel')->update($this->invoiceId, [
            'pdf_path'     => $path,
            'pdf_ready_at' => date('Y-m-d H:i:s'),
        ]);
    }

    /**
     * PDF 生成失敗時的回調。
     */
    protected function onFailure(\Throwable $e): void
    {
        log_message('error', "Invoice PDF generation failed: {$e->getMessage()}", [
            'invoice_id' => $this->invoiceId,
        ]);
    }
}

與 codeigniter4/tasks 整合

若使用官方 CI4 Tasks 套件進行排程:

<?php

declare(strict_types=1);

namespace Config;

use CodeIgniter\Tasks\Config\Tasks as BaseTask;

final class Tasks extends BaseTask
{
    public function run(): void
    {
        // 每日凌晨 2 點批次生成月結報告
        $this->call(function () {
            $pendingInvoices = model('InvoiceModel')->where('pdf_ready_at', null)->findAll();
            foreach ($pendingInvoices as $invoice) {
                (new \App\Jobs\GenerateInvoicePdfJob($invoice->id))->execute();
            }
        })->daily('02:00');
    }
}

與 codeigniter4-queue 整合

若使用 lonnieezell/codeigniter-queue 等第三方佇列套件:

// 推送至佇列
$queue = service('queue');
$queue->push('pdf', 'generate_invoice', [
    'job_class'  => \App\Jobs\GenerateInvoicePdfJob::class,
    'invoice_id' => $invoiceId,
]);
<?php

declare(strict_types=1);

namespace App\Jobs;

use NextPDF\CodeIgniter\Jobs\GeneratePdfJob;

// 實作第三方佇列套件的 Job 介面
final class QueueableInvoicePdfJob extends GeneratePdfJob implements \Queue\JobInterface
{
    public function execute(array $data): bool
    {
        $this->invoiceId = $data['invoice_id'];
        return parent::run(); // 呼叫 GeneratePdfJob 的執行邏輯
    }
}

重試機制

final class GenerateInvoicePdfJob extends GeneratePdfJob
{
    // 最大重試次數
    protected int $maxRetries = 3;

    // 重試延遲(秒,指數退避)
    protected array $retryDelays = [30, 120, 300];

    // 應觸發重試的例外類型
    protected array $retryOn = [
        \NextPDF\Core\Exception\RenderingException::class,
    ];
}

進度回報

對於多頁長文件,可透過資料庫或快取回報生成進度:

protected function buildDocument(Document $document): void
{
    $pages = $this->getPageData();
    $total = count($pages);

    foreach ($pages as $index => $pageData) {
        $document->addPage();
        $document->text($pageData['content'], x: 20, y: 30);

        // 每 10 頁更新一次進度
        if ($index % 10 === 0) {
            cache()->save(
                "pdf_progress_{$this->jobId}",
                ['processed' => $index + 1, 'total' => $total],
                300, // TTL 5 分鐘
            );
        }
    }
}

CLI Worker 啟動

# 使用 spark 指令啟動 Worker(若使用 CI4 Tasks)
php spark tasks:run

# 使用 Supervisor 管理 Worker(生產環境建議)
# /etc/supervisor/conf.d/nextpdf-worker.conf
[program:nextpdf-worker]
command=php /var/www/html/spark tasks:run
directory=/var/www/html
autostart=true
autorestart=true
numprocs=2
stderr_logfile=/var/log/nextpdf-worker.err.log
stdout_logfile=/var/log/nextpdf-worker.out.log

參見