Een certificaat is alleen waardevol als mensen het vertrouwen. Wanneer een cursist een PDF bij een sollicitatie voegt, moet een hiring manager binnen enkele seconden kunnen bevestigen dat deze echt is. Laravel biedt je de tools om dat vertrouwen zelf op te bouwen, zonder de controle uit handen te geven aan een extern platform. Je hebt de volledige controle over de branding, de data en de verificatieketen van begin tot eind.

Het doel is simpel: genereer een professionele PDF die er scherp uitziet wanneer deze wordt geprint, een scanbare QR-code bevat voor directe verificatie, en een uniek record opslaat dat niet geraden of vervalst kan worden. Hier lees je hoe je dit opzet.

Begin bij de database-laag

Gebruik geen auto-incrementerende ID's voor certificaten. Een ID zoals 8473 is gemakkelijk te raden. Iemand zou door de nummers op je verificatiepagina kunnen bladeren en alle credentials die je ooit hebt uitgegeven kunnen scrapen. Genereer in plaats daarvan een UUID wanneer je het certificaatrecord aanmaakt.

Sla de UUID op als een string in je migratie. Wanneer een gebruiker een cursus voltooit, trigger dan een event dat een Certificate-model aanmaakt dat gekoppeld is aan de gebruiker en de cursus. Houd het verificatie-endpoint volledig gescheiden van elk geauthenticeerd dashboard. Een publieke route zoals /verify/{uuid} moet het record opzoeken en de naam van de ontvanger, de cursusnaam en de voltooiingsdatum weergeven. Als de UUID niet bestaat, retourneer dan een 404. Er is geen extra login nodig.

Deze ene ontwerpkeuze beschermt de privacy en voorkomt enumeratie-aanvallen.

Het ontwerpen van de Blade-template

Het ontwerp van je certificaat bevindt zich in een standaard Blade-view, maar je moet er anders mee omgaan dan met een normale webpagina. PDF-rendering engines gedragen zich niet als browsers. Externe stylesheets zijn onbetrouwbaar omdat de PDF-converter ze mogelijk niet in dezelfde context ophaalt. Gebruik uitsluitend inline CSS.

Denk in fysieke afmetingen. Als je een certificaat in liggende stand (landscape) wilt, stel dan de breedte en hoogte van de container direct in:

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

Positioneer handtekeningen, zegels en randen met absolute positionering binnen die container. Web-safe lettertypen zijn je veiligste keuze, hoewel sommige PDF-engines het insluiten van lettertypen toestaan als je voorzichtig bent. Vermijd een zware afhankelijkheid van CSS Grid of geavanceerde Flexbox, tenzij je weet dat je rendering engine dit ondersteunt. Voor brede compatibiliteit lossen ouderwetse table-layouts nog steeds uitlijningsproblemen op die moderne CSS niet kan garanderen binnen een PDF-engine.

Houd de markup schoon. Hoe eenvoudiger de HTML, hoe kleiner de kans dat de converter je verrast met een kapot ontwerp.

Een QR-code toevoegen

Installeer het Simple QRCode-pakket. Je hebt een QR-code nodig die rechtstreeks naar je verificatiepagina verwijst. Render deze in je Blade-template als volgt:

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

Wanneer een werkgever die code met een telefoon scant, komt hij terecht in je Laravel-app en ziet hij het live record. Die brug tussen fysiek en digitaal is wat het certificaat verifieerbaar maakt. Een mooie PDF alleen bewijst niets. De QR-code maakt het vertrouwen direct en visueel.

Een PDF-engine kiezen

Laravel-ontwikkelaars kiezen meestal een van de drie paden. Elk pad heeft duidelijke voor- en nadelen.

DomPDF draait volledig in PHP. Installeer het via Composer, geef de door Blade gerenderde HTML door en sla de output op. Het vereist geen extra serversoftware, wat het aantrekkelijk maakt als je op shared hosting zit. Het nadeel is de CSS-ondersteuning. Moderne layout-tools zoals Flexbox en Grid worden ofwel slechts gedeeltelijk ondersteund of werken niet goed. Custom webfonts kunnen lastig zijn en complexe achtergronden worden vaak onjuist gerenderd. Als het ontwerp van je certificaat sober is, werkt DomPDF prima. Als je precisie nodig hebt, zal het je frustreren.

Browsershot hanteert een andere aanpak. Het gebruikt Puppeteer om een headless Chrome-instantie aan te sturen, je HTML precies zo te renderen als de browser die ziet, en een PDF te exporteren. Omdat het echte Chrome is, worden je Tailwind-stijlen, custom fonts en responsive layouts perfect vertaald. Het addertje onder het gras is de serverconfiguratie. Je moet Node.js, Puppeteer en Chrome geïnstalleerd hebben. Op sommige platforms moet je de Chrome-sandbox uitschakelen of het geheugen zorgvuldig beheren om vastgelopen processen te voorkomen. Als je je eigen server beheert en een pixel-perfecte reproductie van het ontwerp nodig hebt, is Browsershot bijna niet te verslaan.

HTML naar PDF API is de derde route. Je verzamelt je gerenderde Blade HTML als een string, stuurt deze via een POST-verzoek naar een externe service en ontvangt een PDF terug. Het praktische voordeel is dat er geen server-side afhankelijkheid is. Je hoeft geen Chrome te installeren. Je hoeft geen PHP-lettertype-onregelmatigheden te debuggen. Je krijgt een correct geformatteerde vector-PDF terug. De meeste van deze services rekenen per document, maar voor veel toepassingen is de operationele eenvoud de kosten waard. Als je een hoge kwaliteit output wilt zonder sysadmin-werk, is dit meestal de snelste weg naar productie.

De volledige workflow

Zodra je een engine hebt gekozen, is de pipeline eenvoudig. Een event voor het voltooien van een cursus moet een listener triggeren die het volgende doet:

  1. Genereer een UUID en maak het Certificate-record aan.
  2. Render de Blade-view naar een HTML-string met behulp van view('certificates.pdf', compact('certificate'))->render().
  3. Stuur die HTML-string naar je gekozen PDF-service of pakket.
  4. Sla het resulterende bestand op in storage/app/certificates/ achter een logisch pad, zoals certificates/2024/06/uuid.pdf.
  5. Voeg het bestand toe aan een notificatie en e-mail dit automatisch naar de leerling.

Het notificatiesysteem van Laravel handelt de e-mail netjes af. Zet het generatieproces in een queue als het renderen langer dan een seconde duurt. Je wilt niet dat een leerling moet wachten op een PDF tijdens het laden van een pagina.

Wanneer de gebruiker het bestand downloadt, krijgt hij een standaard PDF die in elke viewer wordt geopend. Wanneer hij deze print, blijft de vectortekst scherp. Wanneer een werkgever de QR-code scant, bevestigt je Laravel-app de UUID tegen de database en toont de gegevens.

Waarom vector-output onmisbaar is

Sommige methoden voor PDF-generatie produceren gerasterde output. Dat betekent dat elk stukje tekst een afbeelding wordt. Zoom in en de letters worden wazig. Bestandsgroottes exploderen. Schermlezers en zoekmachines kunnen de tekst niet extraheren.

Controleer altijd of de door jou gekozen methode een echte vector-PDF produceert. De tekst moet selecteerbaar blijven. De lijnen moeten scherp blijven bij 400 procent zoom. Je leerlingen verdienen een document dat officieel aanvoelt, en geen screenshot dat als PDF is opgeslagen.

Vector-output houdt ook je opslagkosten laag. Een pagina met vectortekst weegt misschien vijftig kilobytes. Dezelfde pagina als een afbeelding met een hoge resolutie kan meer dan twee megabytes zijn. Op schaal maakt dat verschil.

Een les die je kunt toepassen

Dit in-house bouwen gaat niet alleen over het besparen van geld op een SaaS-certificaatplatform. Het gaat om geloofwaardigheid. Wanneer een leerling een document kan printen, het over een bureau kan schuiven en een vreemde dit direct met een telefoon kan verifiëren, heb je ze iets blijvends gegeven. Jij hebt de controle over het ontwerp. Jij hebt de controle over de gegevens. Het vertrouwen hoort bij jouw merk, niet bij het subdomein van een leverancier.

Hoe ga jij om met de generatie en verificatie van certificaten in je eigen Laravel-applicaties? Ik hoor graag in de reacties wat voor jou heeft gewerkt.