Рано или поздно каждый веб-разработчик сталкивается с задачей: «А сделай-ка кнопку, чтобы счет в PDF скачивался». И если с HTML все понятно, то с PDF начинается квест. В Yii2 есть несколько путей решения, и сегодня мы разберем их по косточкам.

Digital-студия WNDER

Почему не стоит генерировать 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.

Правило: Всегда указывайте кодировку UTF-8 в настройках mPDF. Иначе русские буквы превратятся в кракозябры. И не забудьте про шрифт с поддержкой кириллицы — например, 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 в файл и отдавайте готовый. Это снимет нагрузку с сервера.
Лайфхак: Для отладки 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».

Удачи в генерации! Пусть ваши счета всегда сходятся, а отчеты не падают.

Студия WNDER