Blog

📄 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!

Wsparcie istniejącego systemu

Potrzebujesz pomocy z działającą aplikacją?

Pomagam firmom rozwijać działające systemy, porządkować wdrożenia i dodawać nowe funkcje bez dokładania chaosu do projektu.

Komentarze (0)
Zaloguj się, aby dodać komentarz

Musisz być zalogowany, aby dodać komentarz.

Zaloguj się

Potrzebujesz kogoś, kto weźmie odpowiedzialność za kolejny krok?

Porozmawiajmy o Twoim projekcie i określmy zakres, który ma sens dla Twoich celów.