📄 Porównanie generatorów PDF w Laravel

W tym artykule porównujemy popularne rozwiązania do generowania PDF w Laravel.

📋 Spis treści

Wprowadzenie

Ten artykuł prezentuje implementację dwóch popularnych rozwiązań do generowania PDF-ów w Laravel: Spatie Laravel PDF (z Browsershot) oraz barryvdh/laravel-dompdf. Oba rozwiązania mają swoje mocne strony i ograniczenia - poniżej znajdziesz instrukcje konfiguracji, przykłady użycia, porównanie oraz rekomendacje, kiedy używać każdego z nich.

Instalacja i konfiguracja

1. Spatie Laravel PDF (Browsershot)

  • Instalacja pakietu:
    composer require spatie/laravel-pdf
    npm install puppeteer
    npx puppeteer browsers install chrome
    
  • Upewnij się, że Chromium jest zainstalowany w obrazie Docker PHP (jeśli używasz Dockera):
    RUN apt-get update \
        && apt-get install -y wget gnupg2 \
        && wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
        && echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list \
        && apt-get update \
        && apt-get install -y google-chrome-stable \
        && apt-get clean \
        && rm -rf /var/lib/apt/lists/*
    

2. barryvdh/laravel-dompdf

  • Instalacja pakietu:
    composer require barryvdh/laravel-dompdf
    php artisan vendor:publish --provider="Barryvdh\DomPDF\ServiceProvider"
    
  • Nie wymaga dodatkowych zależności systemowych. Stylowanie ograniczone do podstawowego CSS.

Przykładowe trasy i kontroler

W routes/web.php:

Route::get('/pdf/spatie', [PdfDemoController::class, 'spatie'])->name('pdf.spatie');
Route::get('/pdf/dompdf', [PdfDemoController::class, 'dompdf'])->name('pdf.dompdf');

W app/Http/Controllers/PdfDemoController.php:

<?php

declare(strict_types=1);

namespace App\Http\Controllers;

use Barryvdh\DomPDF\Facade\Pdf as DomPdf;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Spatie\Browsershot\Browsershot;
use Spatie\LaravelPdf\Facades\Pdf;

class PdfDemoController extends Controller
{
    public function spatie(Request $request)
    {
        $data = [
            'user' => [
                'name' => 'Jan Kowalski',
                'email' => '[email protected]',
            ],
            'items' => [
                ['name' => 'Super Produkt', 'qty' => 2, 'price' => 199.99],
                ['name' => 'Mega Usługa', 'qty' => 1, 'price' => 499.00],
            ],
            'total' => 199.99 * 2 + 499.00,
        ];

        return Pdf::view('pdfs.spatie-invoice', $data)
            ->withBrowsershot(function (Browsershot $browsershot): void {
                $browsershot->noSandbox()->format('A4');
            })
            ->download('faktura-spatie.pdf');
    }

    public function dompdf(Request $request): Response
    {
        $data = [
            'invoice_number' => 'FV/2024/05/001',
            'date' => now()->toDateString(),
            'client' => 'Anna Nowak',
            'items' => [
                ['name' => 'Produkt A', 'qty' => 3, 'price' => 100],
                ['name' => 'Produkt B', 'qty' => 1, 'price' => 250],
            ],
            'total' => 3 * 100 + 250,
        ];

        return DomPdf::loadView('pdfs.dompdf-invoice', $data)->download('faktura-dompdf.pdf');
    }
}

Przykładowe widoki

Spatie Laravel PDF (Tailwind, nowoczesny układ)

resources/views/pdfs/layouts/pdf.blade.php:

<!DOCTYPE html>
<html lang="pl">
<head>
    <meta charset="UTF-8">
    <title>@yield('title', 'PDF')</title>
    <style>
        body { font-family: 'Inter', sans-serif; }
    </style>
    @yield('styles')
    <script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-100 p-8">
@yield('content')
</body>
</html>

resources/views/pdfs/spatie-invoice.blade.php:

@extends('pdfs.layouts.pdf')

@section('title', 'Faktura (Spatie Laravel PDF)')

@section('content')
<div class="max-w-2xl mx-auto bg-white rounded shadow p-8">
    <div class="flex justify-between items-center mb-8">
        <div>
            <h1 class="text-2xl font-bold text-gray-800">FAKTURA</h1>
            <p class="text-gray-500">Sprzedawca: <span class="font-semibold">{{ $user['name'] }}</span></p>
            <p class="text-gray-500">Email: {{ $user['email'] }}</p>
        </div>
        <div class="text-right">
            <p class="text-gray-500">Data: {{ now()->toDateString() }}</p>
            <p class="text-gray-500">Nr faktury: FV/{{ now()->format('Y') }}/{{ now()->format('m') }}/001</p>
        </div>
    </div>
    <table class="w-full mb-8 text-sm border">
        <thead>
        <tr class="bg-gray-200">
            <th class="p-2 border">#</th>
            <th class="p-2 border text-left">Produkt</th>
            <th class="p-2 border">Ilość</th>
            <th class="p-2 border">Cena jedn.</th>
            <th class="p-2 border">Razem</th>
        </tr>
        </thead>
        <tbody>
        @foreach($items as $i => $item)
        <tr class="border-b">
            <td class="p-2 border text-center">{{ $i+1 }}</td>
            <td class="p-2 border">{{ $item['name'] }}</td>
            <td class="p-2 border text-center">{{ $item['qty'] }}</td>
            <td class="p-2 border text-right">{{ number_format($item['price'], 2) }} zł</td>
            <td class="p-2 border text-right">{{ number_format($item['qty'] * $item['price'], 2) }} zł</td>
        </tr>
        @endforeach
        </tbody>
        <tfoot>
        <tr>
            <td colspan="4" class="p-2 border text-right font-bold">Suma</td>
            <td class="p-2 border text-right font-bold">{{ number_format($total, 2) }} zł</td>
        </tr>
        </tfoot>
    </table>
    <div class="text-xs text-gray-400 mt-8">Wygenerowano automatycznie przez Spatie Laravel PDF + TailwindCSS</div>
</div>
@endsection

DomPDF (prosty układ)

resources/views/pdfs/dompdf-invoice.blade.php:

@extends('pdfs.layouts.pdf')

@section('title', 'Faktura (DomPDF)')
@section('styles')
<style>
    body { font-family: DejaVu Sans, sans-serif; }
    table { border-collapse: collapse; width: 100%; margin-bottom: 20px; }
    th, td { border: 1px solid #ccc; padding: 6px 10px; }
    th { background: #eee; }
    .text-right { text-align: right; }
    .text-center { text-align: center; }
</style>
@endsection
@section('content')
<body>
<h2>FAKTURA (DomPDF)</h2>
<p>Nr faktury: <strong>{{ $invoice_number }}</strong></p>
<p>Data: <strong>{{ $date }}</strong></p>
<p>Klient: <strong>{{ $client }}</strong></p>
<table>
    <thead>
    <tr>
        <th>#</th>
        <th>Produkt</th>
        <th>Ilość</th>
        <th>Cena jedn.</th>
        <th>Razem</th>
    </tr>
    </thead>
    <tbody>
    @foreach($items as $i => $item)
    <tr>
        <td class="text-center">{{ $i+1 }}</td>
        <td>{{ $item['name'] }}</td>
        <td class="text-center">{{ $item['qty'] }}</td>
        <td class="text-right">{{ number_format($item['price'], 2) }} zł</td>
        <td class="text-right">{{ number_format($item['qty'] * $item['price'], 2) }} zł</td>
    </tr>
    @endforeach
    </tbody>
    <tfoot>
    <tr>
        <td colspan="4" class="text-right"><strong>Suma</strong></td>
        <td class="text-right"><strong>{{ number_format($total, 2) }} zł</strong></td>
    </tr>
    </tfoot>
</table>
<p style="font-size: 10px; color: #888;">Wygenerowano automatycznie przez barryvdh/laravel-dompdf</p>
</body>
@endsection

Porównanie i rekomendacje

FunkcjaSpatie Laravel PDF (Browsershot)barryvdh/laravel-dompdf
SilnikHeadless Chrome (Puppeteer)DomPDF (PHP)
Wymaga Chrome/ChromiumTakNie
StylowaniePełny CSS, Tailwind, grid, flexboxPodstawowy CSS
Nowoczesne układyTakOgraniczone
Jakość renderowaniaDoskonałaDobra/Średnia
SzybkośćWolniejsza (uruchamianie Chrome)Szybka (czysty PHP)
Wsparcie dla JSTak (ograniczone)Nie
Wsparcie dla SVGTakOgraniczone
Złożoność konfiguracjiŚrednia (Docker, Node, Chrome)Bardzo prosta
Kompatybilność z Docker/CITak (z noSandbox)Tak

Kiedy używać?

  • Spatie Laravel PDF (Browsershot): Gdy potrzebujesz pięknych, nowoczesnych PDF-ów, chcesz używać Tailwind, grid, flexbox, SVG, a Twoje środowisko wspiera Chrome/Chromium (np. Docker, dedykowany serwer). Użyj withBrowsershot(), aby dostosować opcje dla Dockera/CI.
  • barryvdh/laravel-dompdf: Gdy potrzebujesz prostych PDF-ów, chcesz szybkiej konfiguracji i nie potrzebujesz zaawansowanych funkcji CSS lub JS (np. proste faktury, potwierdzenia).

Podsumowanie

Oba rozwiązania mają swoje miejsce w ekosystemie Laravel. Dla nowoczesnych projektów, gdzie liczy się design i elastyczność układu, wybierz Spatie Laravel PDF (z Browsershot). Dla prostych przypadków użycia, gdzie kluczowa jest szybkość i prostota, barryvdh/laravel-dompdf jest wystarczające.

Kod źródłowy


Śledź mnie na LinkedIn, aby otrzymywać więcej wskazówek o Laravel i DevOps!

Czy chciałbyś dowiedzieć się więcej o generowaniu PDF-ów w Laravel? Daj znać w komentarzach poniżej!

Komentarze (0)
Zostaw komentarz

© 2025 Wszelkie prawa zastrzeżone.