CodeIgniter 快速開始¶
本指南說明如何在 CodeIgniter 4.6 專案中整合 nextpdf/codeigniter,包含 Services 配置、便利 Helper 函數,以及非同步 Queue Job 的使用方式。
前置條件: - CodeIgniter 4.6.x 專案 - PHP 8.5+ - 已完成 安裝指南 中的基本設定
Backport 相容性說明¶
PHP 8.5 語法需求:
nextpdf/codeigniter使用 PHP 8.5 語法特性。若你的 CodeIgniter 應用程式執行於 PHP 8.1,請改用
nextpdf/backport套件, 並參閱 PHP 相容性說明。
步驟一:安裝套件¶
nextpdf/codeigniter 透過 CodeIgniter 4 的 Composer 自動探索機制, 自動在框架啟動時初始化 Helper 與 Services。
步驟二:設定 Services¶
在你的 app/Config/Services.php 中,nextpdf/codeigniter 已自動透過 Composer Psr-4 autoloading 將自身的 Service 定義注入 CodeIgniter 的服務容器。
若需要自訂設定,可建立 app/Config/NextPdf.php:
<?php
declare(strict_types=1);
namespace Config;
use CodeIgniter\Config\BaseConfig;
final class NextPdf extends BaseConfig
{
/**
* Path to custom font directory.
*
* @var non-empty-string
*/
public string $fontPath = WRITEPATH . 'nextpdf/fonts';
/**
* Font cache directory.
*
* @var non-empty-string
*/
public string $fontCache = WRITEPATH . 'cache/nextpdf/fonts';
/**
* Enable Spectrum accelerator sidecar.
*/
public bool $spectrumEnabled = false;
/**
* Spectrum socket DSN.
*
* @var non-empty-string
*/
public string $spectrumSocket = 'tcp://localhost:9000';
/**
* Default storage base path for saved PDFs.
*
* @var non-empty-string
*/
public string $storagePath = WRITEPATH . 'pdfs';
}
步驟三:使用 Services¶
透過 CodeIgniter 的 Services 靜態方法取得 PdfFactory:
<?php
declare(strict_types=1);
namespace App\Controllers;
use CodeIgniter\HTTP\ResponseInterface;
use NextPDF\CodeIgniter\Services;
final class InvoiceController extends BaseController
{
public function download(int $invoiceId): ResponseInterface
{
$factory = Services::pdfFactory();
$document = $factory->create();
$document->addPage()
->setFont(family: 'NotoSans', size: 12)
->text("發票編號:INV-{$invoiceId}", x: 20, y: 30)
->text('金額:NT$ 1,000', x: 20, y: 45)
->text('日期:' . date('Y-m-d'), x: 20, y: 60);
// 取得 PDF 二進位內容
$content = $document->output();
return $this->response
->setHeader('Content-Type', 'application/pdf')
->setHeader('Content-Disposition', "inline; filename=\"invoice-{$invoiceId}.pdf\"")
->setBody($content);
}
}
步驟四:使用 Helper 函數¶
載入 NextPDF Helper 後,可使用 pdf() 與 pdf_document() 快速函數:
<?php
declare(strict_types=1);
namespace App\Controllers;
use CodeIgniter\HTTP\ResponseInterface;
final class ReportController extends BaseController
{
public function __construct()
{
// 載入 NextPDF Helper
helper('nextpdf');
}
public function generate(): ResponseInterface
{
// pdf() — 建立並直接輸出 PDF Response
return pdf(function (\NextPDF\Core\Document $doc): void {
$doc->addPage()
->setFont(family: 'NotoSans', size: 16)
->text('月度銷售報表', x: 20, y: 30)
->setFont(family: 'NotoSans', size: 12)
->text('期間:' . date('Y-m'), x: 20, y: 50);
// <!-- PLACEHOLDER:CONTENT:report-body — Report table, charts, summary -->
}, filename: 'report-' . date('Ym') . '.pdf');
}
public function saveToFile(): ResponseInterface
{
// pdf_document() — 建立 Document 實例,由呼叫者控制儲存方式
$document = pdf_document(function (\NextPDF\Core\Document $doc): void {
$doc->addPage()
->text('存檔範例', x: 20, y: 30);
});
$path = WRITEPATH . 'pdfs/report-' . date('Ymd') . '.pdf';
$document->save($path);
return $this->response->setJSON(['saved' => true, 'path' => $path]);
}
}
Helper 函數簽章¶
/**
* Create a PDF and return a CodeIgniter ResponseInterface.
*
* @param callable(\NextPDF\Core\Document): void $builder
* @param non-empty-string $filename
* @param 'inline'|'attachment' $disposition
*/
function pdf(
callable $builder,
string $filename = 'document.pdf',
string $disposition = 'inline',
): \CodeIgniter\HTTP\ResponseInterface {}
/**
* Create and return a NextPDF Document instance.
*
* @param callable(\NextPDF\Core\Document): void $builder
*/
function pdf_document(
callable $builder,
): \NextPDF\Core\Document {}
步驟五:在 View 中使用¶
<?php
declare(strict_types=1);
namespace App\Controllers;
use CodeIgniter\HTTP\ResponseInterface;
use NextPDF\CodeIgniter\Services;
final class ContractController extends BaseController
{
public function preview(int $contractId): ResponseInterface
{
$factory = Services::pdfFactory();
// 先用 CodeIgniter View 渲染 HTML
$html = view('pdfs/contract', [
'contract_id' => $contractId,
'date' => date('Y-m-d'),
'client' => 'Acme Corp.',
]);
// 從 HTML 建立 PDF(需 nextpdf/artisan)
$document = $factory->createFromHtml($html);
$content = $document->output();
return $this->response
->setHeader('Content-Type', 'application/pdf')
->setHeader('Content-Disposition', "inline; filename=\"contract-{$contractId}.pdf\"")
->setBody($content);
}
}
對應的 View 檔案 app/Views/pdfs/contract.php:
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<style>
body { font-family: 'Noto Sans TC', sans-serif; margin: 40px; }
h1 { color: #1E3A8A; }
</style>
</head>
<body>
<h1>合約編號:<?= esc($contract_id) ?></h1>
<p>日期:<?= esc($date) ?></p>
<p>乙方:<?= esc($client) ?></p>
<!-- PLACEHOLDER:CONTENT:contract-template-body — Full contract body -->
</body>
</html>
步驟六:Queue Job 非同步生成¶
CodeIgniter 4 的 Tasks 或第三方 Queue 套件可用於非同步 PDF 生成:
使用 codeigniter4/tasks(排程)¶
<?php
declare(strict_types=1);
namespace App\Tasks;
use CodeIgniter\Tasks\Task;
use NextPDF\CodeIgniter\Services;
use Psr\Log\LoggerInterface;
final class MonthlyReportTask extends Task
{
public function run(): void
{
$factory = Services::pdfFactory();
$document = $factory->create();
$month = date('Y-m', strtotime('first day of last month'));
$document->addPage()
->setFont(family: 'NotoSans', size: 20)
->text("月度報表 {$month}", x: 20, y: 30);
// <!-- PLACEHOLDER:CONTENT:monthly-report-content — Monthly report pages -->
$outputPath = WRITEPATH . "pdfs/monthly-report-{$month}.pdf";
$document->save($outputPath);
log_message('info', "月度報表已生成:{$outputPath}");
}
}
Queue Job(使用 codeigniter4-queue)¶
<?php
declare(strict_types=1);
namespace App\Jobs;
use CodeIgniter\Queue\BaseJob;
use CodeIgniter\Queue\Interfaces\JobInterface;
use NextPDF\CodeIgniter\Services;
final class GeneratePdfJob extends BaseJob implements JobInterface
{
/**
* @param array{user_id: int, report_month: non-empty-string} $data
*/
public function process(): bool
{
$userId = $this->job->payload['user_id'];
$reportMonth = $this->job->payload['report_month'];
$factory = Services::pdfFactory();
$document = $factory->create();
$document->addPage()
->setFont(family: 'NotoSans', size: 20)
->text("報表 {$reportMonth}(使用者 {$userId})", x: 20, y: 30);
// <!-- PLACEHOLDER:CONTENT:job-pdf-content — PDF content builder -->
$path = WRITEPATH . "pdfs/user-{$userId}/{$reportMonth}.pdf";
@mkdir(dirname($path), 0755, recursive: true);
$document->save($path);
return true;
}
}
分派 Job:
// 在 Controller 中分派
service('queue')
->push('default', 'GeneratePdf', [
'user_id' => $this->request->getPost('user_id'),
'report_month' => date('Y-m'),
]);
路由設定¶
// app/Config/Routes.php
$routes->group('api', ['namespace' => 'App\Controllers'], function ($routes) {
$routes->get('invoices/(:num)/pdf', 'InvoiceController::download/$1');
$routes->post('reports/generate', 'ReportController::generate');
$routes->get('contracts/(:num)/preview', 'ContractController::preview/$1');
});
測試¶
<?php
declare(strict_types=1);
namespace App\Tests\Controllers;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\FeatureTestTrait;
use NextPDF\CodeIgniter\Testing\PdfFactoryFake;
use NextPDF\CodeIgniter\Services;
final class InvoiceControllerTest extends CIUnitTestCase
{
use FeatureTestTrait;
protected function setUp(): void
{
parent::setUp();
// 注入 Fake PdfFactory
Services::injectMock('pdfFactory', new PdfFactoryFake());
}
public function test_invoice_pdf_download_returns_pdf_response(): void
{
$result = $this->get('api/invoices/1/pdf');
$result->assertOK();
$result->assertHeader('Content-Type', 'application/pdf');
}
}
下一步¶
- 安裝指南 — 商業套件(Pro / Enterprise)整合
- 快速開始(Core) — 純 PHP 範例
- 架構說明 — PdfFactory vs createStandalone()
- Core API 參考