Minggu, 28 January 2024

    Mendesain PDF dengan Tailwind CSS di Laravel

    Mendesain PDF di Laravel menggunakan Tailwind CSS: Teknik 2024 untuk integrasi dan estetika modern, dari instalasi hingga implementasi.

    Dalam pelajaran kali ini, kita akan belajar bagaimana cara mendesain pdf di laravel menggunakan tailwind css.

    Setup Laravel

    Kita akan benar-benar mulai dari awal pastinya. Pertama sekali, silakan lakukan instalasi laravel nya dengan perintah berikut.

    Terminal
    laravel new laravel-pdf
    

    Setelah itu, silakan cd laravel-pdf untuk melanjutkan instalasi package dari spatie yaitu laravel-pdf dengan menjalankan perintah berikut:

    Terminal
    composer require spatie/laravel-pdf
    

    Karena package ini menggunakan browsershot, maka kita perlu juga instalasi package pembantunya melalui yaitu puppeteer.

    Terminal
    npm i puppeteer
    

    Mari Kita Pakai

    Untuk tutorial kali ini, karena kita tidak ada setup tailwindcss, jadi sangat sah untuk kita menggunakan cdn.

    Buat Controller

    Dalam hal ini, saya akan mencontohkan untuk membuat invoice yang kira-kira akan di download seorang user nantinya dalam bentuk pdf. Maka oleh karena itu, yang pertama kita akan membuat controller baru dengan nama InvoiceController. Buka terminal Anda dan silakan jalankan perintah berikut:

    Terminal
    php artisan make:controller InvoiceController
    

    Setelah itu, silakan buat 2 method sekaligus yaitu index dan juga download seperti berikut:

    InvoiceController.php
    public function index()
    {
        return view('invoices/index');
    }
    

    Untuk download nya ini lumayan panjang, karena di sini kita tidak ada persiap data, jadi datanya saya hard code saja sebagai contoh.

    InvoiceController.php
    public function download()
    {
        $items = collect([
            [
                'title' => 'Website redesign',
                'description' => 'Redesign the company website with a fresh look.',
                'hours' => 50,
                'rate' => 125000,
                'price' => 50 * 125000,
            ],
            [...],
            [...],
        ]);
    
        return pdf()
            ->view('pdf.invoice', [
                'items' => $items,
                'subtotal' => $items->sum('price'),
                'tax' => $items->sum('price') * 0.1,
                'total' => $items->sum('price'),
            ])
            ->download(downloadName: 'invoice-'.now()->format('Y-m-d').'.pdf');
    }
    

    Tanda [...] bisa kalian ganti dengan data yang format nya sama seperti yang di atas. Sehingga seakan-akan item nya lebih dari 1. Perhatikan pada function pdf(), ini tidak bisa dipakai sebelum kita import di atas, maka oleh karena itu, pastikan Anda telah menambahnya seperti berikut:

    InvoiceController.php
    use function Spatie\LaravelPdf\Support\pdf;
    
    class InvoiceController extends Controller
    {
        public function index(...)
    
        public function download(...)
    }
    

    Views

    Pertama sekali, kita akan langsung membuat view untuk index nya, buat folder dengan nama invoices tepat di dalam views, dan di dalamnya buat 1 file dengan index.blade.php, isi nya simpel saja, bisa langsung dibuat seperti berikut:

    invoices/index.blade.php
    <!doctype html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Your Invoices</title>
        <script src="https://cdn.tailwindcss.com"></script>
    </head>
    <body class="antialiased tracking-tight py-24">
        <main class="max-w-screen-lg mx-auto text-sm">
            <div class="border p-6 shadow-sm max-w-xl bg-white rounded-lg">
                <p class="mb-2">
                    Your invoices ready to download, click the link below to download.
                </p>
    
                <a class="underline text-blue-600" href='/invoices/download' target='_blank'>
                    Download Invoice
                </a>
            </div>
        </main>
    </body>
    </html>
    

    Melalui halaman ini, user akan mendownload invoice nya.

    Setelah itu, silakan buat 1 folder dengan nama pdf, dan di dalam nya buat 1 file dengan nama invoice.blade.php, isi nya bisa dibuat seperti ini saja.

    pdf/invoice.blade.php
    <!doctype html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Invoice {{ now()->format('d/m/Y') }}</title>
        <script src="https://cdn.tailwindcss.com"></script>
    </head>
    <body>
    <div class="text-[0.8rem] p-24">
        <div class="sm:flex sm:items-center">
            <div class="sm:flex-auto">
                <h1 class="text-base font-semibold leading-6 text-gray-900">Invoice</h1>
                <p class="mt-2 text-gray-600">For work completed from
                    <time datetime="2024-01-01">{{ now()->firstOfMonth()->format('d M Y') }}</time>
                    to
                    <time datetime="2024-31-01">{{ now()->lastOfMonth()->format('d M Y') }}</time>
                    .
                </p>
            </div>
        </div>
        <div class="-mx-4 mt-8 flow-root sm:mx-0">
            <table class="min-w-full">
                <colgroup>
                    <col class="w-full sm:w-1/2">
                    <col class="sm:w-1/6">
                    <col class="sm:w-1/6">
                    <col class="sm:w-1/6">
                </colgroup>
                <thead class="border-b border-gray-300 text-gray-900">
                <tr>
                    <th scope="col" class="py-3.5 pl-4 pr-3 text-left font-semibold text-gray-900 sm:pl-0">Project
                    </th>
                    <th scope="col" class="hidden px-3 py-3.5 text-right font-semibold text-gray-900 sm:table-cell">
                        Hours
                    </th>
                    <th scope="col" class="hidden px-3 py-3.5 text-right font-semibold text-gray-900 sm:table-cell">
                        Rate
                    </th>
                    <th scope="col" class="py-3.5 pl-3 pr-4 text-right font-semibold text-gray-900 sm:pr-0">Price
                    </th>
                </tr>
                </thead>
                    <tbody>
                    @foreach($items as $item)
                        <tr class="border-b border-gray-200">
                            <td class="max-w-0 py-5 pl-4 pr-3 sm:pl-0">
                                <div class="font-medium text-gray-900">{{ $item['title'] }}</div>
                                <div class="mt-1 text-gray-500">{{ $item['description'] }}</div>
                            </td>
                            <td class="hidden px-3 py-5 text-right font-mono text-gray-500 sm:table-cell">{{ $item['hours'] }}</td>
                            <td class="hidden px-3 py-5 text-right font-mono text-gray-500 sm:table-cell">{{ Number::currency($item['rate'], 'IDR', 'id') }}</td>
                            <td class="py-5 pl-3 pr-4 text-right font-mono text-gray-500 sm:pr-0">{{ Number::currency($item['price'], 'IDR', 'id') }}</td>
                        </tr>
                    @endforeach
                    </tbody>
                <tfoot>
                <tr>
                    <th scope="row" colspan="3"
                        class="hidden pl-4 pr-3 pt-6 text-right font-normal text-gray-500 sm:table-cell sm:pl-0">
                        Subtotal
                    </th>
                    <th scope="row" class="pl-4 pr-3 pt-6 text-left font-normal text-gray-500 sm:hidden">Subtotal
                    </th>
                    <td class="pl-3 pr-4 pt-6 text-right text-gray-500 sm:pr-0">
                        <span class="font-mono">{{ Number::currency($subtotal, 'IDR', 'id') }}</span>
                    </td>
                </tr>
                <tr>
                    <th scope="row" colspan="3"
                        class="hidden pl-4 pr-3 pt-4 text-right font-normal text-gray-500 sm:table-cell sm:pl-0">Tax
                    </th>
                    <th scope="row" class="pl-4 pr-3 pt-4 text-left font-normal text-gray-500 sm:hidden">Tax</th>
                    <td class="pl-3 pr-4 pt-4 text-right text-gray-500 sm:pr-0">
                        <span class="font-mono">{{ Number::currency($tax, 'IDR', 'id') }}</span>
                    </td>
                </tr>
                <tr>
                    <th scope="row" colspan="3"
                        class="hidden pl-4 pr-3 pt-4 text-right font-semibold text-gray-900 sm:table-cell sm:pl-0">
                        Total
                    </th>
                    <th scope="row" class="pl-4 pr-3 pt-4 text-left font-semibold text-gray-900 sm:hidden">Total
                    </th>
                    <td class="pl-3 pr-4 pt-4 text-right font-semibold text-gray-900 sm:pr-0">
                        <span class="font-mono">{{ Number::currency($total, 'IDR', 'id') }}</span>
                    </td>
                </tr>
                </tfoot>
            </table>
        </div>
    </div>
    </body>
    </html>
    

    Ini adalah desain yang akan menampilkan table dari invoice user.

    Route

    Setelah view dan controller nya kita siapkan, maka selanjutnya kita bisa membuat route nya, buka file routes/web.php dan kemudian silakan tambahkan 2 route ini:

    routes/web.php
    use App\Http\Controllers\InvoiceController;
    
    /* ... */
    
    Route::get('invoices', [InvoiceController::class, 'index']);
    Route::get('invoices/download', [InvoiceController::class, 'download']);
    

    Dengan begitu, sekarang kita bisa terminal dan silakan jalankan aplikasinya, jika Anda menggunakan herd, maka itu bisa dengan langsung mengunjungi laravel-pdf.test/invoices atau jika tidak, bisa jalan dev server nya dengan menjalankan perintah berikut:

    Terminal
    php artisan serve
    

    Dan Anda bisa langsung kunjungi localhost:8000/invoices. Anda langsung bisa tekan download invoice dan harusnya akan langsung mendownload file dengan nama misalnya invoice-2024-01-27-9.pdf

    Production

    Jika project kalian sudah di production, dan kalian juga menggunakan ubuntu, kalian > bisa menjalankan perintah berikut ini:

    Temrinal
    curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -
    sudo apt-get install -y nodejs gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgbm1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget libgbm-dev libxshmfence-dev
    sudo npm install --location=global --unsafe-perm puppeteer@^17
    sudo chmod -R o+rx /usr/lib/node_modules/puppeteer/.local-chromium
    

    Kesimpulan

    Tanpa menggunakan package seperti spatie/laravel-pdf, memang masih bisa membuat PDF di Laravel, namun integrasi Tailwind CSS menjadi tantangan. Tailwind CSS, yang bergantung pada sistem build modern dan JavaScript, sulit diimplementasikan dalam PDF yang biasanya memerlukan CSS murni.

    Fitur seperti Flexbox dan utilities Tailwind lainnya tidak akan berfungsi secara langsung dalam pembuatan PDF tanpa alat seperti Browsershot yang memungkinkan rendering HTML dan CSS layaknya browser. Jadi, penggunaan package ini memudahkan integrasi Tailwind CSS dalam desain PDF di Laravel.

    Jika Anda suka dengan artikel, maka silakan share ke teman-teman agar mereka mengetaui bagaimana cara design pdf dengan tailwindcss di laravel.

    Karteil
    Destinasi Utama Belajar Online dengan Format Tulisan yang Elegan
    Kunjungi Sekarang

    Irsyad A. Panjaitan

    Let's start living like no one can help us in any event, so that when we are helped in certain times, it becomes a plus in itself.

    Go to Irsyad A. Panjaitan profile
    Support me
    SaweriaGithub