Certyfikat ma znaczenie tylko wtedy, gdy ludzie mu ufają. Gdy uczeń dołącza plik PDF do aplikacji o pracę, menedżer zatrudniający powinien móc w kilka sekund potwierdzić jego autentyczność. Laravel daje Ci narzędzia, aby samodzielnie budować to zaufanie, bez oddawania kontroli zewnętrznej platformie. Możesz kontrolować branding, dane i cały proces weryfikacji od początku do końca.

Cel jest prosty: wygenerować profesjonalny plik PDF, który wygląda świetnie po wydruku, zawiera skanowalny kod QR do natychmiastowej weryfikacji i przechowuje unikalny rekord, którego nie da się odgadnąć ani sfałszować. Oto jak to zrealizować.

Zacznij od warstwy bazy danych

Nie używaj identyfikatorów typu auto-increment dla certyfikatów. ID takie jak 8473 jest łatwe do odgadnięcia. Ktoś mógłby przechodzić przez kolejne liczby na Twojej stronie weryfikacyjnej i pobrać wszystkie wystawione przez Ciebie poświadczenia. Zamiast tego, podczas tworzenia rekordu certyfikatu, wygeneruj UUID.

W migracji przechowuj UUID jako ciąg znaków (string). Gdy użytkownik ukończy kurs, wywołaj zdarzenie (event), które utworzy model Certificate powiązany z użytkownikiem i kursem. Trzymaj punkt końcowy (endpoint) weryfikacji całkowicie oddzielony od jakiegokolwiek uwierzytelnionego panelu administracyjnego. Publiczna trasa, taka jak /verify/{uuid}, powinna wyszukiwać rekord i wyświetlać imię i nazwisko odbiorcy, tytuł kursu oraz datę ukończenia. Jeśli UUID nie istnieje, zwróć błąd 404. Nie jest wymagane dodatkowe logowanie.

Ten pojedynczy wybór projektowy chroni prywatność i zapobiega atakom typu enumeration (wyliczanie identyfikatorów).

Projektowanie szablonu Blade

Projekt certyfikatu znajduje się w standardowym widoku Blade, ale musisz traktować go inaczej niż zwykłą stronę internetową. Silniki renderujące PDF nie zachowują się jak przeglądarki. Zewnętrzne arkusze stylów są niepewne, ponieważ konwerter PDF może ich nie pobrać w tym samym kontekście. Używaj wyłącznie stylów CSS inline.

Myśl w wymiarach fizycznych. Jeśli chcesz certyfikat w orientacji poziomej, ustaw szerokość i wysokość kontenera bezpośrednio:

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

Pozycjonuj podpisy, pieczęcie i obramowania za pomocą pozycjonowania absolutnego (absolute positioning) wewnątrz tego kontenera. Bezpieczne czcionki internetowe (web-safe fonts) to najpewniejszy wybór, choć niektóre silniki PDF pozwalają na osadzanie czcionek, jeśli zachowasz ostrożność. Unikaj nadmiernego polegania na CSS Grid lub zaawansowanym Flexbox, chyba że wiesz, że Twój silnik renderujący to obsługuje. Dla szerokiej kompatybilności, tradycyjne układy oparte na tabelach (table) wciąż rozwiązują problemy z wyrównaniem, których nowoczesny CSS nie może zagwarantować wewnątrz silnika PDF.

Dbaj o czystość kodu HTML. Im prostszy kod HTML, tym mniejsza szansa, że konwerter zaskoczy Cię uszkodzonym układem.

Dodawanie kodu QR

Zainstaluj pakiet Simple QRCode. Potrzebujesz kodu QR, który prowadzi bezpośrednio do Twojej strony weryfikacyjnej. W szablonie Blade wyrenderuj go w następujący sposób:

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

Gdy pracodawca zeskanuje ten kod telefonem, trafi do Twojej aplikacji Laravel i zobaczy żywy rekord. To fizyczne połączenie ze światem cyfrowym sprawia, że certyfikat jest weryfikowalny. Sam piękny plik PDF niczego nie dowodzi. Kod QR sprawia, że zaufanie staje się natychmiastowe i wizualne.

Wybór silnika PDF

Deweloperzy Laravel zazwyczaj wybierają jedną z trzech ścieżek. Każda z nich ma swoje wady i zalety.

DomPDF działa całkowicie w PHP. Zainstaluj go przez Composera, przekaż wyrenderowany w Blade HTML i zapisz wynik. Nie wymaga dodatkowego oprogramowania serwerowego, co czyni go atrakcyjnym, jeśli korzystasz z hostingu współdzielonego. Wadą jest wsparcie CSS. Nowoczesne narzędzia układu, takie jak Flexbox i Grid, są albo częściowo wspierane, albo nie działają poprawnie. Własne czcionki internetowe (webfonts) mogą sprawiać problemy, a złożone tła często renderują się nieprawidłowo. Jeśli projekt certyfikatu jest zachowawczy, DomPDF sprawdzi się dobrze. Jeśli potrzebujesz precyzji, będzie Cię frustrować.

Browsershot przyjmuje inne podejście. Używa Puppeteer do sterowania bezgłową instancją Chrome, aby wyrenderować HTML dokładnie tak, jak widzi go przeglądarka, a następnie wyeksportować PDF. Ponieważ jest to prawdziwy Chrome, Twoje style Tailwind, własne czcionki i responsywne układy zostaną przeniesione idealnie. Haczykiem jest konfiguracja serwera. Musisz mieć zainstalowane Node.js, Puppeteer i Chrome. Na niektórych platformach musisz wyłączyć piaskownicę (sandbox) Chrome lub ostrożnie zarządzać pamięcią, aby zapobiec zawieszaniu się procesów. Jeśli masz kontrolę nad swoim serwerem i potrzebujesz idealnej replikacji projektu (pixel-perfect), Browsershot jest bezkonkurencyjny.

HTML to PDF API is the third route. You collect your rendered Blade HTML as a string, POST it to an external service, and receive a PDF back. The practical benefit is zero server-side dependency. You do not install Chrome. You do not debug PHP font quirks. You get back a properly formatted vector PDF. Most of these services charge per document, but for many applications, the operational simplicity is worth the cost. If you want high-quality output without sysadmin work, this is usually the fastest path to production.

The Full Workflow

Once you have picked your engine, the pipeline is straightforward. A course completion event should trigger a listener that does the following:

  1. Generate a UUID and create the Certificate record.
  2. Render the Blade view to an HTML string using view('certificates.pdf', compact('certificate'))->render().
  3. Pass that HTML string to your chosen PDF service or package.
  4. Save the resulting file to storage/app/certificates/ behind a logical path, such as certificates/2024/06/uuid.pdf.
  5. Attach the file to a notification and email it to the learner automatically.

Laravel’s notification system handles the email cleanly. Queue the generation job if rendering takes more than a second. You do not want a learner waiting on a PDF during a page load.

When the user downloads the file, they get a standard PDF that opens in any viewer. When they print it, the vector text remains crisp. When an employer scans the QR code, your Laravel app confirms the UUID against the database and displays the facts.

Why Vector Output Is Non-Negotiable

Some PDF generation methods produce rasterized output. That means every piece of text becomes an image. Zoom in and the letters blur. File sizes explode. Screen readers and search engines cannot extract the text.

Always verify that your chosen method produces a true vector PDF. The text should remain selectable. The lines should stay sharp at 400 percent zoom. Your learners deserve a document that feels official, not a screenshot saved as a PDF.

Vector output also keeps your storage costs down. A page of vector text might weigh fifty kilobytes. The same page as a high-resolution image could exceed two megabytes. At scale, that difference matters.

A Takeaway You Can Use

Building this in-house is not just about saving money on a SaaS certificate platform. It is about credibility. When a learner can print a document, hand it across a desk, and let a stranger verify it instantly with a phone, you have given them something permanent. You control the design. You control the data. The trust belongs to your brand, not a vendor’s subdomain.

How do you handle certificate generation and verification in your own Laravel applications? I would like to hear what has worked for you in the comments.