Laravel 快速開始¶
本指南說明如何在 Laravel 12 專案中整合 nextpdf/laravel,包含同步與非同步(Queue) 兩種 PDF 生成模式。
前置條件: - Laravel 12.x 專案 - PHP 8.5+ - 已完成 安裝指南 中的基本設定
Backport 相容性說明¶
PHP 8.5 語法需求:
nextpdf/laravel使用 PHP 8.5 語法特性。若你的 Laravel 應用程式執行於 PHP 8.1,請改用
nextpdf/backport套件, 並參閱 PHP 相容性說明 了解 Backport 與 Laravel 的整合方式。
步驟一:安裝套件¶
nextpdf/laravel 透過 Laravel 的 Package Auto-Discovery 機制自動註冊 Service Provider, 無需手動修改 config/app.php。
步驟二:發佈設定檔(選用)¶
這會建立 config/nextpdf.php:
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Font Directory
|--------------------------------------------------------------------------
| Path to custom font directory. NextPDF will search this directory
| in addition to its built-in font registry.
*/
'fonts' => [
'path' => storage_path('fonts'),
'cache' => storage_path('framework/cache/nextpdf/fonts'),
],
/*
|--------------------------------------------------------------------------
| Spectrum Accelerator
|--------------------------------------------------------------------------
*/
'spectrum' => [
'enabled' => env('SPECTRUM_ENABLED', false),
'socket' => env('SPECTRUM_SOCKET', 'tcp://localhost:9000'),
],
/*
|--------------------------------------------------------------------------
| Default PDF Metadata
|--------------------------------------------------------------------------
*/
'metadata' => [
'creator' => env('APP_NAME', 'NextPDF'),
],
/*
|--------------------------------------------------------------------------
| Storage Disk
|--------------------------------------------------------------------------
| Laravel filesystem disk used for PDF output.
*/
'disk' => env('NEXTPDF_DISK', 'local'),
];
步驟三:使用 Facade¶
nextpdf/laravel 提供 Pdf Facade,封裝 DocumentFactory,支援跨 Request 的 Font Registry 共享。
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use Illuminate\Http\Response;
use NextPDF\Laravel\Facades\Pdf;
final class InvoiceController extends Controller
{
public function download(int $invoiceId): Response
{
// 建立文件
$document = Pdf::create()
->addPage()
->setFont(family: 'NotoSans', size: 12)
->text("發票編號:INV-{$invoiceId}", x: 20, y: 30)
->text('金額:NT$ 1,000', x: 20, y: 45)
->text('日期:' . now()->format('Y-m-d'), x: 20, y: 60);
// 以 PdfResponse 回傳(自動設定正確的 Content-Type 標頭)
return $document->toPdfResponse(
filename: "invoice-{$invoiceId}.pdf",
disposition: 'inline', // 或 'attachment' 強制下載
);
}
}
步驟四:使用 PdfFactory(依賴注入)¶
在需要更精細控制或進行單元測試時,建議透過 DI Container 注入 PdfFactory:
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use Illuminate\Http\Response;
use NextPDF\Laravel\Contracts\PdfFactory;
final class ReportController extends Controller
{
public function __construct(
private readonly PdfFactory $pdfFactory,
) {}
public function generate(): Response
{
$document = $this->pdfFactory->create();
$document->addPage()
->setFont(family: 'NotoSans', size: 16)
->text('月度報表', x: 20, y: 30);
// 儲存至 Laravel Storage
$path = 'pdfs/report-' . now()->format('Ym') . '.pdf';
$document->saveToStorage(disk: 'local', path: $path);
return response()->json(['path' => $path]);
}
}
步驟五:Blade View 整合¶
使用 Blade 模板作為 PDF 內容來源(透過 Artisan 套件的 Chrome CDP 渲染):
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use Illuminate\Http\Response;
use NextPDF\Laravel\Facades\Pdf;
final class ContractController extends Controller
{
public function preview(int $contractId): Response
{
$data = [
'contract' => Contract::findOrFail($contractId),
'company' => config('app.name'),
];
// 使用 Blade 模板渲染(需安裝 nextpdf/artisan)
return Pdf::view('pdfs.contract', $data)
->toPdfResponse(filename: "contract-{$contractId}.pdf");
}
}
對應的 Blade 模板 resources/views/pdfs/contract.blade.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>合約編號:{{ $contract->id }}</h1>
<p>甲方:{{ $company }}</p>
<p>乙方:{{ $contract->client_name }}</p>
<!-- PLACEHOLDER:CONTENT:contract-template-body — Full contract template content -->
</body>
</html>
注意:Blade 渲染模式需安裝
nextpdf/artisan並設定 Chrome 執行環境。
步驟六:Queue Job 非同步生成¶
對於大型文件或批次生成,建議使用 Laravel Queue:
<?php
declare(strict_types=1);
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use NextPDF\Laravel\Contracts\PdfFactory;
final class GenerateMonthlyReportJob implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
/**
* Maximum number of retry attempts.
*/
public int $tries = 3;
/**
* Timeout in seconds.
*/
public int $timeout = 120;
public function __construct(
private readonly int $userId,
private readonly string $reportMonth, // Format: 'YYYY-MM'
) {}
public function handle(PdfFactory $factory): void
{
$document = $factory->create();
$document->addPage()
->setFont(family: 'NotoSans', size: 20)
->text("月度報表 {$this->reportMonth}", x: 20, y: 30);
// <!-- PLACEHOLDER:CONTENT:report-content — Report page building logic -->
$path = "reports/user-{$this->userId}/{$this->reportMonth}.pdf";
$document->saveToStorage(disk: 's3', path: $path);
// 通知使用者(依需求實作)
// ReportGeneratedNotification::dispatch($this->userId, $path);
}
public function failed(\Throwable $exception): void
{
// 記錄失敗原因
logger()->error('PDF 生成失敗', [
'user_id' => $this->userId,
'report_month' => $this->reportMonth,
'error' => $exception->getMessage(),
]);
}
}
分派 Job:
// 立即分派至預設 Queue
GenerateMonthlyReportJob::dispatch(
userId: auth()->id(),
reportMonth: now()->format('Y-m'),
);
// 延遲分派(3 秒後執行)
GenerateMonthlyReportJob::dispatch(userId: 1, reportMonth: '2026-03')
->delay(now()->addSeconds(3));
// 指定 Queue 名稱(高優先度)
GenerateMonthlyReportJob::dispatch(userId: 1, reportMonth: '2026-03')
->onQueue('pdf-high');
路由設定範例¶
// routes/web.php
use App\Http\Controllers\InvoiceController;
use App\Http\Controllers\ReportController;
Route::middleware(['auth'])->group(function () {
Route::get('/invoices/{invoice}/pdf', [InvoiceController::class, 'download'])
->name('invoices.pdf');
Route::post('/reports/generate', [ReportController::class, 'generate'])
->name('reports.generate');
});
測試¶
nextpdf/laravel 提供 Fake 實作,方便在不實際生成 PDF 的情況下測試業務邏輯:
<?php
declare(strict_types=1);
namespace Tests\Feature;
use NextPDF\Laravel\Testing\PdfFake;
use NextPDF\Laravel\Facades\Pdf;
use Tests\TestCase;
final class InvoiceControllerTest extends TestCase
{
public function test_invoice_pdf_download_returns_pdf_response(): void
{
Pdf::fake();
$response = $this->actingAs($this->user())
->get(route('invoices.pdf', ['invoice' => 1]));
$response->assertOk();
$response->assertHeader('Content-Type', 'application/pdf');
Pdf::assertCreated();
Pdf::assertPageCount(1);
}
}
下一步¶
- 安裝指南 — 商業套件(Pro / Enterprise)整合
- 快速開始(Core) — 不使用框架的純 PHP 範例
- 架構說明 — DocumentFactory vs createStandalone() 的選擇
- Core API 參考