はじめに
LaravelでPDFを出力する方法です。
完成版のソースは以下に置いてあります。
https://github.com/naga3/snappy-sample
Laravelで使えるPDF出力ライブラリ
- Laravel Browsershot: https://github.com/verumconsilium/laravel-browsershot
- Puppeteerを使う
- 正確さでは一番か?
- Laravel DOMPDF: https://github.com/barryvdh/laravel-dompdf
- DOMPDFのLaravelラッパー
- PHPだけあれば動くのでお手軽
- Laravel Snappy: https://github.com/barryvdh/laravel-snappy
- Qt WebKitレンダリングエンジンを使ってPDFを出力するツールであるwkhtmltopdfのPHPラッパーであるSnappyのLaravelラッパー(ややこしい)
- Laravel DOMPDFより高速
今回はバランスを考えてLaravel Snappyを選択しました。
Laravelプロジェクト作成
コチラを参考に適当なプロジェクトを作成します。Composerがインストールされていれば簡単に作成できます。
composer create-project --prefer-dist laravel/laravel snappy-sample
cd snappy-sample
Laravel Snappyのインストール
Laravel Snappyのドキュメントを見て進めて行きます。
まずwkhtmltopdfをインストールしますが、こちらもComposerで簡単に導入できます。WindowsやMacなど、バイナリが適合しない場合は上記ドキュメントを参照してください。
composer require h4cc/wkhtmltopdf-amd64 0.12.x
次にLaravel Snappy本体をインストールします。
composer require barryvdh/laravel-snappy
configファイルを作ります。
php artisan vendor:publish --provider="Barryvdh\Snappy\ServiceProvider"
configファイルにwkhtmltopdfの場所を書きます。上記のようにComposerからインストールした場合は
'binary' => base_path('vendor/h4cc/wkhtmltopdf-amd64/bin/wkhtmltopdf-amd64'),
PDF出力のテスト
正常にインストールできたかチェックしてみます。
Route::get('/hello', function () {
return PDF::loadHTML('<h1>Hello!</h1>')->inline();
});
大丈夫そうですね。
Bladeを使ってPDFを出力する。
Snappyは、BladeにそのままHTMLを書いて出力できるので大変楽です。CSSも2くらいまでは正確に効きます。
まずRouteの部分です。
Route::get('/members', function () {
$member = Faker\Factory::create('ja_JP');
return PDF::loadView('members', compact('member'))->inline();
});
Fakerでランダムなデータを生成してviewに渡しています。
次にテンプレートの部分です。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>名簿</title>
<style>
table {
border-collapse: collapse;
}
tr {
page-break-inside: avoid;
}
th,
td {
border: 1px solid black;
}
</style>
</head>
<body>
<h1>名簿</h1>
<table>
<tr>
<th>No.</th>
<th>氏名</th>
<th>住所</th>
<th>電話番号</th>
<th>備考</th>
@for ($no = 1; $no <= 50; $no++) <tr>
<td>{{ $no }}</td>
<td style="white-space: nowrap">{{ $member->name }}</td>
<td>{{ $member->address }}</td>
<td>{{ $member->phoneNumber }}</td>
<td>{{ $member->realText }}</td>
</tr>
@endfor
</table>
</body>
</html>
ブラウザで確認してみます。
良さそうですね。
tr要素にpage-break-inside: avoid;
を適用することによって、セルの途中で改ページされなくなります。
こんな感じ。