Сертифікат має значення лише тоді, коли йому довіряють. Коли учень додає PDF-файл до заявки на роботу, менеджер із найму має мати можливість підтвердити його справжність за лічені секунди. Laravel надає інструменти, щоб ви могли самостійно побудувати таку довіру, не передаючи контроль сторонній платформі. Ви можете повністю володіти брендингом, даними та ланцюжком верифікації від початку до кінця.

Мета проста: згенерувати професійний PDF-файл, який має чіткий вигляд при друці, містить QR-код для миттєвої перевірки та зберігає унікальний запис, який неможливо вгадати або підробити. Ось як це реалізувати.

Почніть із рівня бази даних

Не використовуйте ID з автоінкрементом для сертифікатів. ID на кшталт 8473 легко вгадати. Хтось може перебирати числа на вашій сторінці верифікації та збирати (scrape) усі видані вами документи. Замість цього генеруйте UUID під час створення запису сертифіката.

У вашій міграції зберігайте UUID як рядок. Коли користувач завершує курс, запускайте подію (event), яка створює модель Certificate, пов'язану з користувачем і курсом. Тримайте ендпоінт верифікації повністю відокремленим від будь-якої автентифікованої панелі керування. Публічний маршрут, наприклад /verify/{uuid}, має шукати запис і відображати ім'я отримувача, назву курсу та дату завершення. Якщо UUID не існує, поверніть 404. Жодного додаткового входу в систему не потрібно.

Це єдине дизайнерське рішення захищає приватність і запобігає атакам перебором (enumeration attacks).

Проєктування Blade-шаблону

Дизайн вашого сертифіката зберігається у стандартному Blade view, але до нього слід ставитися інакше, ніж до звичайної вебсторінки. Рушії рендерингу PDF працюють не так, як браузери. Зовнішні таблиці стилів є ненадійними, оскільки конвертер PDF може не завантажити їх у тому самому контексті. Використовуйте виключно вбудований (inline) CSS.

Думайте у фізичних розмірах. Якщо ви хочете сертифікат в альбомній орієнтації, встановіть ширину та висоту контейнера безпосередньо:

<div style="width: 11in; height: 8.5in; position: relative; padding: 40px; font-family: Georgia, serif;">

Розміщуйте підписи, печатки та рамки за допомогою абсолютного позиціювання всередині цього контейнера. Веб-безпечні шрифти — найнадійніший варіант, хоча деякі рушії PDF дозволяють вбудовувати шрифти, якщо діяти обережно. Уникайте сильної залежності від CSS Grid або складного Flexbox, якщо не впевнені, що ваш рушій рендерингу їх підтримує. Для забезпечення широкої сумісності застаріла верстка за допомогою table все ще вирішує проблеми з вирівнюванням, які сучасний CSS не може гарантувати всередині рушія PDF.

Тримайте розмітку чистою. Чим простіший HTML, тим менша ймовірність того, що конвертер здивує вас поламаною версткою.

Додавання QR-коду

Встановіть пакет Simple QRCode. Вам потрібен QR-код, який веде безпосередньо на вашу сторінку верифікації. У вашому Blade-шаблоні відрендеріть його так:

<img src="{!! QrCode::size(150)->generate(route('certificates.verify', $certificate->uuid)) !!}" style="position: absolute; bottom: 40px; right: 40px;">

Коли роботодавець сканує цей код телефоном, він потрапляє у ваш додаток Laravel і бачить актуальний запис. Цей міст між фізичним і цифровим світами робить сертифікат верифікованим. Один лише гарний PDF нічого не доводить. QR-код робить довіру миттєвою та наочною.

Вибір рушія PDF

Розробники Laravel зазвичай обирають один із трьох шляхів. Кожен має свої реальні компроміси.

DomPDF працює повністю на PHP. Встановіть його через Composer, передайте йому HTML, відрендерений у Blade, і збережіть результат. Він не потребує додаткового програмного забезпечення на сервері, що робить його привабливим для спільного хостингу. Мінус — підтримка CSS. Сучасні інструменти верстки, такі як Flexbox і Grid, або підтримуються лише частково, або працюють некоректно. Кастомні вебшрифти можуть бути примхливими, а складні фони часто рендериться неправильно. Якщо дизайн вашого сертифіката стриманий, DomPDF підійде. Якщо ж вам потрібна точність, він вас розчарує.

Browsershot використовує інший підхід. Він використовує Puppeteer для керування headless-інстансом Chrome, щоб відрендерити ваш HTML саме так, як його бачить браузер, і експортувати PDF. Оскільки це справжній Chrome, ваші стилі Tailwind, кастомні шрифти та адаптивна верстка переносяться ідеально. Підвох полягає в налаштуванні сервера. Вам потрібно встановити Node.js, Puppeteer і Chrome. На деяких платформах необхідно вимкнути пісочницю (sandbox) Chrome або ретельно керувати пам'яттю, щоб запобігти зависанню процесів. Якщо ви контролюєте свій сервер і вам потрібне піксельно точне відтворення дизайну, Browsershot важко перемогти.

HTML to PDF API — це третій шлях. Ви збираєте відрендерений Blade HTML як рядок, надсилаєте його методом POST на зовнішній сервіс і отримуєте PDF у відповідь. Практична перевага полягає у відсутності залежностей на стороні сервера. Вам не потрібно встановлювати Chrome. Вам не потрібно відлагоджувати дивацтва зі шрифтами в PHP. Ви отримуєте належним чином відформатований векторний PDF. Більшість цих сервісів стягують плату за кожен документ, але для багатьох застосунків операційна простота варта цих витрат. Якщо вам потрібен високоякісний результат без роботи системного адміністратора, це зазвичай найшвидший шлях до запуску в продакшн.

Повний робочий процес

Щойно ви обрали свій рушій, конвеєр стає зрозумілим. Подія завершення курсу має запускати слухача (listener), який виконує наступне:

  1. Згенерувати UUID та створити запис Certificate.
  2. Відрендерити Blade-представлення в HTML-рядок за допомогою view('certificates.pdf', compact('certificate'))->render().
  3. Передати цей HTML-рядок обраному PDF-сервісу або пакету.
  4. Зберегти отриманий файл у storage/app/certificates/ за логічним шляхом, наприклад certificates/2024/06/uuid.pdf.
  5. Прикріпити файл до сповіщення та автоматично надіслати його учневі електронною поштою.

Система сповіщень Laravel чисто обробляє надсилання електронних листів. Поставте завдання на генерацію в чергу (queue), якщо рендеринг займає більше секунди. Ви ж не хочете, щоб учень чекав на PDF під час завантаження сторінки.

Коли користувач завантажує файл, він отримує стандартний PDF, який відкривається в будь-якому переглядачі. При друку векторний текст залишається чітким. Коли роботодавець сканує QR-код, ваш додаток Laravel перевіряє UUID у базі даних і відображає факти.

Чому векторний вивід є обов'язковим

Деякі методи генерації PDF створюють растерізований вивід. Це означає, що кожен фрагмент тексту стає зображенням. При збільшенні літери розмиваються. Розмір файлів різко зростає. Програми для читання з екрана (screen readers) та пошукові системи не можуть витягти текст.

Завжди перевіряйте, щоб обраний вами метод створював справжній векторний PDF. Текст має залишатися доступним для виділення. Лінії мають залишатися чіткими при 400-відсотковому масштабуванні. Ваші учні заслуговують на документ, який виглядає офіційно, а не на скриншот, збережений як PDF.

Векторний вивід також допомагає знизити витрати на зберігання. Сторінка векторного тексту може важити п'ятдесят кілобайт. Та сама сторінка у вигляді зображення високої роздільної здатності може перевищувати два мегабайти. При масштабуванні ця різниця має значення.

Висновок, який ви можете використати

Розробка цього рішення власними силами — це не лише про економію грошей на SaaS-платформах для сертифікатів. Це про довіру. Коли учень може роздрукувати документ, передати його особисто, а сторонній чоловік може миттєво перевірити його за допомогою телефону, ви даєте йому щось постійне. Ви контролюєте дизайн. Ви контролюєте дані. Довіра належить вашому бренду, а не піддомену постачальника.

А як ви обробляєте генерацію та перевірку сертифікатів у своїх застосунках Laravel? Я б хотів почути про ваш досвід у коментарях.