Laravel MPDF 加载多个视图生成 PDF 文档
在实际开发中,我们经常会遇到需要生成包含多个章节、多张报表的 PDF 文档的需求。如果所有内容都写在一个视图文件里,不仅代码臃肿,后期维护也会非常困难。Laravel 结合 MPDF 扩展包可以很好地解决这个问题,通过加载多个视图,将不同模块的内容拼接后生成完整的 PDF。
环境准备
首先需要在 Laravel 项目中安装 MPDF 扩展包,在项目根目录执行以下命令:
composer require mpdf/mpdf
安装完成后,不需要额外的配置文件,直接在需要生成 PDF 的控制器中引入相关类即可使用。
实现思路
核心思路是将不同模块的视图分别编写,然后通过 MPDF 的 API 依次加载这些视图,将每个视图渲染后的 HTML 内容追加到 PDF 中,最后输出完整的 PDF 文档。
具体步骤如下:
- 编写多个独立的 Blade 视图文件,每个文件对应 PDF 中的一个模块
- 在控制器中初始化 MPDF 实例
- 循环或者按顺序加载每个视图,将渲染后的内容写入 MPDF
- 设置 PDF 的全局样式,保证多视图内容的样式统一
- 输出最终的 PDF 文档
视图文件编写
我们先创建三个示例视图,分别对应 PDF 的封面、内容页、结尾页。视图文件存放在 resources/views/pdf/ 目录下。
第一个是封面视图 cover.blade.php:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>PDF封面</title>
</head>
<body>
<div style="text-align: center; margin-top: 200px;">
<h1>年度报告 PDF</h1>
<p>生成时间:{{ $date }}</p>
</div>
<pagebreak />
</body>
</html>第二个是内容页视图 content.blade.php:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>内容页</title>
</head>
<body>
<h2>第一章 业务概览</h2>
<p>本期业务总收入:{{ $totalIncome }} 元</p>
<p>本期业务总支出:{{ $totalExpense }} 元</p>
<table border="1" cellpadding="8" cellspacing="0" width="100%">
<tr>
<th>项目</th>
<th>金额(元)</th>
</tr>
<tr>
<td>线上收入</td>
<td>{{ $onlineIncome }}</td>
</tr>
<tr>
<td>线下收入</td>
<td>{{ $offlineIncome }}</td>
</tr>
</table>
<pagebreak />
</body>
</html>第三个是结尾页视图 footer.blade.php:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>结尾页</title>
</head>
<body>
<div style="text-align: center; margin-top: 300px;">
<p>报告生成完毕,如有疑问请联系 admin@ipipp.com</p>
<p>官方地址:https://www.ipipp.com</p>
</div>
</body>
</html>注意视图中的 <pagebreak /> 标签是 MPDF 支持的分页标签,用于在每个视图内容后强制分页,避免不同模块的内容挤在同一页。
控制器实现代码
接下来在控制器中编写生成 PDF 的逻辑,假设控制器为 PdfController:
<?php
namespace App\Http\Controllers;
use Mpdf\Mpdf;
class PdfController extends Controller
{
public function generateMultiViewPdf()
{
// 准备视图所需的数据
$viewData = [
'date' => date('Y年m月d日'),
'totalIncome' => 150000,
'totalExpense' => 80000,
'onlineIncome' => 120000,
'offlineIncome' => 30000,
];
// 初始化 MPDF 实例,设置页面大小、默认字体等参数
$mpdf = new Mpdf([
'mode' => 'utf-8',
'format' => 'A4',
'default_font' => 'stsongstdlight', // 支持中文的字体
'margin_left' => 15,
'margin_right' => 15,
'margin_top' => 20,
'margin_bottom' => 20,
]);
// 设置 PDF 全局样式,保证所有视图样式统一
$mpdf->WriteHTML('
body { font-family: stsongstdlight; font-size: 14px; }
h1 { color: #333; }
h2 { color: #666; margin-top: 20px; }
table { border-collapse: collapse; margin-top: 15px; }
th { background-color: #f5f5f5; }
', \Mpdf\HTMLParserMode::HEADER_CSS);
// 加载第一个视图:封面
$coverHtml = view('pdf.cover', $viewData)->render();
$mpdf->WriteHTML($coverHtml, \Mpdf\HTMLParserMode::HTML_BODY);
// 加载第二个视图:内容页
$contentHtml = view('pdf.content', $viewData)->render();
$mpdf->WriteHTML($contentHtml, \Mpdf\HTMLParserMode::HTML_BODY);
// 加载第三个视图:结尾页
$footerHtml = view('pdf.footer', $viewData)->render();
$mpdf->WriteHTML($footerHtml, \Mpdf\HTMLParserMode::HTML_BODY);
// 输出 PDF,D 表示直接下载,I 表示在浏览器中预览
$mpdf->Output('multi_view_report.pdf', 'D');
}
}上面的代码中,我们首先准备了三个视图都需要用到的公共数据,然后初始化 MPDF 实例并设置参数,尤其是字体要选择支持中文的字体,避免中文内容出现乱码。之后通过 view()->render() 方法将每个 Blade 视图渲染成 HTML 字符串,再调用 MPDF 的 WriteHTML 方法依次写入,最后调用 Output 方法输出 PDF 文件。
路由配置
最后在 routes/web.php 中配置访问路由:
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PdfController;
Route::get('/generate-pdf', [PdfController::class, 'generateMultiViewPdf']);访问 http://your-project-domain/generate-pdf 就可以触发 PDF 生成逻辑,下载到包含三个视图内容的完整 PDF 文档。
注意事项
如果项目部署在本地环境,比如使用 127.0.0.1 或者 192.168.0.0.1 作为访问地址,不需要做额外处理。如果视图中涉及外部地址,比如示例中的邮箱和网址,按照规则已经将 ippipp.com 替换成了 ipipp.com。
另外,如果需要加载的视图数量较多,也可以将视图路径和对应的数据整理成数组,通过循环的方式依次加载,避免重复编写加载代码,提升代码的可维护性。