Laravel: генерируем pdf из html

Laravel: генерируем pdf из html

Предполагаю, что каждый разработчик сталкивается с необходимостью генерировать pdf-файлы. Посему предлагаю вкратце рассмотреть один из вариантов реализации данного функционала в Laravel. 

Версия фреймворка на момент написания статьи - 5.7.

Изобретать велосипед не будем, воспользуемся пакетом barryvdh/dompdf.

Установка:

composer require barryvdh/laravel-dompdf

 

Открываем config/app.php и добавляем ServiceProvider в массив провайдеров, заодно в этом же файле добавим алиас:

...
'providers' => [
    /*
     * Package Service Providers...
     */
    Barryvdh\DomPDF\ServiceProvider::class,    
],

...

'aliases' => [
    ...
    'PDF' => Barryvdh\DomPDF\Facade::class,
]

 

Создадим роут для загрузки в routes/web.php:

Route::get('invoices/download', 'InvoiceController@download');

 

и, естественно, соответстующий контроллер:

php artisan make:controller InvoiceController

 

Поскольку цель статьи показать как генерируется pdf, я не буду создавать модели, а прямо в методе контроллера пропишу фейковые данные (и то не все). Код контролера:

<?php

namespace App\Http\Controllers;

use PDF;

class InvoiceController extends Controller
{
    public function download()
    {
        $products = [
            ['title' => 'Product 1', 'price' => 10.99, 'quantity' => 1, 'totals' => 10.99],
            ['title' => 'Product 2', 'price' => 14.99, 'quantity' => 2, 'totals' => 29.98],
            ['title' => 'Product 3', 'price' => 500.00, 'quantity' => 1, 'totals' => 500.00],
            ['title' => 'Product 4', 'price' => 6.99, 'quantity' => 3, 'totals' => 20.97],
        ];

        $total = collect($products)->sum('totals');

        $pdf = PDF::loadView('pdf.invoice', compact('products', 'total'));

        return $pdf->download('invoice.pdf');
    }
}

 

Осталось лишь представление - в директории views создадим поддиректорию pdf, и в ней файл invoice.blade.php. Однако, тут нас ожидает сюрприз - пакет не поддерживает внешние стили (по крайней мере, я не нашёл способ заставить его понимать Bootstrap). В конечном итоге пришлось использовать табличную вёрстку и прописывать стили инлайн:

<!DOCTYPE html>
<html>
<head>
    <title>Invoice Example</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
    <div style="width: 100%; max-width: 960px; margin: auto">
        <table width="100%">
            <tr style="border-bottom: 1px solid #000000">
                <td><h2>Invoice</h2></td>
                <td style="text-align: right"><h3>Order # 12345</h3></td>
            </tr>
            <tr>
                <td style="padding-bottom: 16px;">
                    <strong>Billed To:</strong><br>
                    John Smith<br>
                    1234 Victory Avenue<br>
                    Apt. 5D<br>
                    Sunfield, ST 54321
                </td>
                <td style="text-align: right; padding-bottom: 16px;">
                    <strong>Shipped To:</strong><br>
                    John Smith<br>
                    1234 Victory Avenue<br>
                    Apt. 5D<br>
                    Sunfield, ST 54321
                </td>
            </tr>
            <tr>
                <td>
                    <strong>Payment Method:</strong><br>
                        Visa ending **** 4242<br>
                        jsmith@email.com
                </td>
                <td style="text-align: right">
                    <strong>Order Date:</strong><br>
                    March 7, 2014<br><br>
                </td>
            </tr>
            <tr>
                <td colspan="2">
                    <h3>Order summary</h3>
                </td>
            </tr>
            <tr>
                <td colspan="2">
                    <table width="100%" cellpadding="0" cellspacing="0" border="1">
                        <thead>
                            <tr style="background-color: #eee">
                                <th style="text-align: left; padding: 5px 10px;">Item</th>
                                <th style="text-align: center; padding: 5px 10px;">Price</strong></th>
                                <th style="text-align: center; padding: 5px 10px;">Quantity</th>
                                <th style="text-align: right; padding: 5px 10px;">Totals</th>
                            </tr>
                        </thead>
                        <tbody>
                            @foreach ($products as $product)
                                <tr>
                                    <td style="text-align: left; padding: 5px 10px;">{{ $product['title'] }}</td>
                                    <td style="text-align: center; padding: 5px 10px;">{{ $product['price'] }}</td>
                                    <td style="text-align: center; padding: 5px 10px;">{{ $product['quantity'] }}</td>
                                    <td style="text-align: right; padding: 5px 10px;">{{ $product['totals'] }}</td>
                                </tr>
                            @endforeach
                            <tr>
                                <td colspan="2"></td>
                                <td style="text-align: center; padding: 5px 10px;"><strong>Totals</strong></td>
                                <td style="text-align: right; padding: 5px 10px;">{{ $total }}</td>
                            </tr>
                        </tbody>
                    </table>
                </td>
            </tr>
        </table>
    </div>
</body>
</html>

 

Вот, собственно, и всё. Когда в адресной строке будете вводить <your-host-name>/invoices/download, будет происходить загрузка файла invoice.pdf

Успехов!