Рано или поздно каждый веб-разработчик сталкивается с задачей: «А сделай-ка кнопку, чтобы счет в PDF скачивался». И если с HTML все понятно, то с PDF начинается квест. В Yii2 есть несколько путей решения, и сегодня мы разберем их по косточкам.
Почему не стоит генерировать PDF «на глазок»
Первое, что приходит в голову — использовать встроенные функции PHP или конвертировать HTML через браузер. Мол, открыл страницу, нажал Ctrl+P, сохранил как PDF. Но когда таких счетов нужно 500 штук в час, ручной метод отпадает. Нужна автоматизация.
В Yii2 есть расширения, которые берут на себя всю грязную работу. Но у них разный подход: одни рисуют PDF с нуля, другие конвертируют HTML в PDF. Выбор зависит от того, что у вас уже есть: готовый HTML-шаблон счета или желание рисовать каждую линию вручную.
Три кита: mPDF, TCPDF и Dompdf
В мире Yii2 прижились три основные библиотеки. Давайте сравним их в таблице, чтобы вы не тратили время на гугление.
| Библиотека | Поддержка CSS | Скорость | Сложность | Русский язык |
|---|---|---|---|---|
| mPDF | Отличная (почти как в браузере) | Средняя | Низкая | Из коробки |
| TCPDF | Слабая (нужны костыли) | Высокая | Высокая | Требует настройки шрифтов |
| Dompdf | Хорошая (но не все теги) | Низкая | Низкая | Работает, но бывают глюки |
Если вам нужно быстро и с версткой — берите mPDF. Если важна скорость на больших объемах и вы готовы помучиться — TCPDF. Dompdf — компромисс, но с ним часто всплывают проблемы с кириллицей.
Установка и настройка mPDF в Yii2
Рассмотрим самый популярный сценарий — использование mPDF через расширение kartik-v/yii2-mpdf. Ставим через composer:
composer require kartik-v/yii2-mpdf
Теперь в контроллере можно создавать PDF. Допустим, у нас есть модель Invoice с данными о заказе. Пишем экшен:
public function actionGeneratePdf($id)
{
$invoice = Invoice::findOne($id);
if (!$invoice) {
throw new NotFoundHttpException('Счет не найден');
}
$content = $this->renderPartial('_invoice_pdf', ['invoice' => $invoice]);
$pdf = new \kartik\mpdf\Pdf([
'mode' => \kartik\mpdf\Pdf::MODE_UTF8,
'format' => \kartik\mpdf\Pdf::FORMAT_A4,
'orientation' => \kartik\mpdf\Pdf::ORIENT_PORTRAIT,
'destination' => \kartik\mpdf\Pdf::DEST_BROWSER,
'filename' => 'invoice_' . $invoice->id . '.pdf',
'cssFile' => '@webroot/css/invoice.css',
]);
$pdf->content = $content;
return $pdf->render();
}
Обратите внимание на параметр destination. Он может быть:
DEST_BROWSER— открыть в браузере (если поддерживается).DEST_DOWNLOAD— скачать файл.DEST_STRING— вернуть PDF как строку (для сохранения на диск).DEST_FILE— сохранить в файл по указанному пути.
Для отправки по почте обычно используют DEST_STRING, а потом прикрепляют к письму через SwiftMailer.
dejavusans.
Генерация PDF из HTML-шаблона: пример верстки
Главный плюс mPDF — он понимает CSS. Можно сверстать счет как обычную HTML-страницу, а потом «распечатать» в PDF. Вот пример упрощенного шаблона _invoice_pdf.php:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
body { font-family: 'dejavusans', sans-serif; font-size: 12px; }
.header { text-align: center; margin-bottom: 30px; }
.table { width: 100%; border-collapse: collapse; }
.table th, .table td { border: 1px solid #ccc; padding: 8px; }
.total { text-align: right; font-weight: bold; }
</style>
</head>
<body>
<div class="header">
<h1>Счет № <?= $invoice->id ?></h1>
<p>от <?= Yii::$app->formatter->asDate($invoice->created_at) ?></p>
</div>
<table class="table">
<thead>
<tr><th>Товар</th><th>Кол-во</th><th>Цена</th><th>Сумма</th></tr>
</thead>
<tbody>
<?php foreach ($invoice->items as $item): ?>
<tr>
<td><?= $item->name ?></td>
<td><?= $item->quantity ?></td>
<td><?= $item->price ?></td>
<td><?= $item->quantity * $item->price ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<p class="total">Итого: <?= $invoice->total ?> руб.</p>
</body>
</html>
Такой шаблон можно рендерить через renderPartial и передавать в mPDF. Главное — не использовать сложные CSS-свойства вроде flex или grid. mPDF их не поймет.
Лайфхаки и подводные камни
Вот несколько советов, которые сэкономят вам часы отладки.
- Шрифты. Если используете кастомный шрифт, его нужно положить в папку
@vendor/mpdf/mpdf/ttfontsи указать в конфиге. Иначе PDF будет пустым или с квадратами. - Изображения. Пути к картинкам должны быть абсолютными. Относительные пути mPDF не понимает.
- Большие таблицы. Если данных много, mPDF может «съесть» часть строк. Увеличьте лимит памяти через
ini_set('memory_limit', '256M'). - Кэширование. Если счет генерируется часто, сохраняйте PDF в файл и отдавайте готовый. Это снимет нагрузку с сервера.
DEST_STRING и сохраняйте результат в файл. Так вы сможете открыть его в просмотрщике и увидеть, что пошло не так, без повторной генерации.
Еще один момент: если вы генерируете PDF с русским текстом, обязательно проверьте, что в настройках mPDF указан autoScriptToLang = true и autoLangToFont = true. Это автоматически подставит нужный шрифт для кириллицы.
Альтернативный путь: генерация через JavaScript
Иногда PDF нужно сформировать на стороне клиента. Например, чтобы не грузить сервер. Тогда в ход идет библиотека jsPDF или pdfmake. В Yii2 можно подключить ее через ассеты и вызвать из JavaScript.
// Пример с jsPDF
const { jsPDF } = window.jspdf;
const doc = new jsPDF();
doc.text('Счет №123', 10, 10);
doc.save('invoice.pdf');
Но учтите: клиентская генерация хороша для простых документов. Для сложной верстки с таблицами и шрифтами лучше использовать серверный подход.
Что в итоге?
Генерация PDF в Yii2 — задача решаемая. Главное — выбрать правильный инструмент. Для 90% проектов хватит mPDF с расширением от Kartik. Он прост, поддерживает CSS и русский язык. Если нужна скорость на потоке — смотрите в сторону TCPDF, но готовьтесь к настройке шрифтов.
Не забывайте про кодировку, шрифты и пути к изображениям. И помните: PDF — это не HTML, чудес не бывает. Тестируйте на реальных данных, а не на «Hello World».
Удачи в генерации! Пусть ваши счета всегда сходятся, а отчеты не падают.

