Laravel 6: Laporan PDF dengan spipu/html2pdf
Membuat laporan data periode dalam format PDF lengkap dengan kop, nomor halaman, tanda tangan, dan QR code menggunakan spipu/html2pdf.
Capaian pembelajaran
Setelah pertemuan ini, mahasiswa mampu:
- Menjelaskan pilihan pustaka laporan PDF di Laravel beserta kelebihan dan kekurangannya
- Memasang pustaka
spipu/html2pdfdengan Composer - Membuat view laporan dengan kop, nomor halaman, dan tanda tangan
- Menampilkan dan mengunduh laporan PDF dari controller
Persiapan
- Proyek dari Pertemuan 5 sudah memiliki CRUD periode.
- Ekstensi PHP gd dan mbstring aktif. Periksa dengan:
php -m
Jika gd tidak ada di daftar (umum terjadi di XAMPP), buka php.ini, cari baris ;extension=gd, hapus tanda ; di depannya, simpan, lalu restart Apache dan terminal.
Materi
Mengapa spipu/html2pdf?
Ada beberapa cara membuat laporan PDF di Laravel. Semuanya mengubah sesuatu (biasanya HTML) menjadi PDF, tetapi cara kerjanya berbeda:
| Pustaka | Cara kerja | Kebutuhan tambahan |
|---|---|---|
| spipu/html2pdf | HTML + tag khusus → TCPDF, murni PHP | Tidak ada (cukup ekstensi gd & mbstring) |
| barryvdh/laravel-dompdf | HTML + CSS → Dompdf, murni PHP | Tidak ada |
| mPDF | HTML + CSS, murni PHP | Tidak ada, ukuran paket lebih besar |
| barryvdh/laravel-snappy | HTML → program wkhtmltopdf |
Harus memasang program wkhtmltopdf di server; proyek ini sudah tidak dikembangkan lagi |
| spatie/browsershot | HTML → Google Chrome (Puppeteer) | Harus ada Node.js dan Chrome di server |
Kelebihan spipu/html2pdf dibanding pilihan lain:
- Murni PHP, tanpa program tambahan. Berbeda dengan Snappy dan Browsershot, tidak perlu memasang
wkhtmltopdf, Node.js, atau Chrome. Karena itu bisa langsung berjalan di XAMPP/Laragon maupun shared hosting yang tidak mengizinkan program eksternal. - Kop dan footer setiap halaman cukup dengan tag. Tag
<page_header>dan<page_footer>otomatis diulang di setiap halaman. Di Dompdf, hal yang sama harus diakali dengan CSSposition: fixedatau script. - Nomor halaman siap pakai. Cukup tulis
[[page_cu]](halaman saat ini) dan[[page_nb]](jumlah halaman), misalnya "Halaman 2 dari 3". Di Dompdf nomor halaman perlu ditulis lewat script PHP di dalam view. - QR code dan barcode bawaan. Tag
<qrcode>dan<barcode>langsung tersedia tanpa paket tambahan. Cocok untuk laporan yang perlu tautan verifikasi. - Pengaturan kertas dalam milimeter. Ukuran kertas, orientasi, dan margin diatur dalam satuan mm, sehingga mudah disesuaikan dengan format surat resmi.
- Berbasis TCPDF yang matang. Mendukung UTF-8, gambar, dan tabel panjang yang otomatis berlanjut ke halaman berikutnya beserta judul kolomnya.
Kekurangannya juga perlu diketahui:
- Dukungan CSS terbatas. Bootstrap,
flex, dangridtidak bisa dipakai. Tata letak disusun dengan<table>. - HTML harus rapi dan tertutup (setiap tag dibuka dan ditutup). HTML yang berantakan menyebabkan error.
- Untuk laporan yang sangat besar (ribuan baris), prosesnya lebih lambat dibanding pustaka berbasis browser.
Untuk laporan administrasi kampus yang umumnya berupa tabel, kop, dan tanda tangan, keterbatasan ini jarang menjadi masalah.
Memasang spipu/html2pdf
Jalankan di folder proyek:
composer require spipu/html2pdf
Perintah ini juga otomatis memasang tecnickcom/tcpdf yang menjadi mesin pembuat PDF-nya.
Menambahkan route
Buka routes/web.php, tambahkan route laporan:
Route::get('/laporan/periode', [PeriodeController::class, 'cetak'])->name('laporan.periode');
Nama route sengaja diawali laporan. (bukan periode.) agar nanti bisa dikelompokkan di menu Laporan pada sidebar.
Method cetak di controller
Buka app/Http/Controllers/PeriodeController.php. Tambahkan use di bagian atas:
use Spipu\Html2Pdf\Html2Pdf;
Lalu tambahkan method cetak:
public function cetak()
{
$semua_periode = Periode::orderBy('singkatan')->get();
// 1. Ubah view Blade menjadi teks HTML
$html = view('periode.cetak', compact('semua_periode'))->render();
// 2. Siapkan PDF: orientasi, ukuran kertas, bahasa, unicode, encoding, margin (kiri, atas, kanan, bawah) dalam mm
$pdf = new Html2Pdf('P', 'A4', 'en', true, 'UTF-8', [15, 10, 15, 10]);
$pdf->pdf->SetTitle('Laporan Data Periode');
// 3. Tulis HTML ke PDF
$pdf->writeHTML($html);
// 4. Kirim ke browser
return response($pdf->output('laporan-periode.pdf', 'S'))
->header('Content-Type', 'application/pdf')
->header('Content-Disposition', 'inline; filename="laporan-periode.pdf"');
}
Penjelasan parameter new Html2Pdf(...):
| Parameter | Nilai | Arti |
|---|---|---|
| Orientasi | 'P' / 'L' |
Portrait (tegak) / Landscape (mendatar) |
| Ukuran | 'A4', 'F4', 'LETTER' |
Ukuran kertas |
| Bahasa | 'en' |
Bahasa pesan error pustaka (tidak memengaruhi isi laporan) |
| Unicode | true |
Agar huruf non-ASCII tampil benar |
| Encoding | 'UTF-8' |
Encoding teks |
| Margin | [15, 10, 15, 10] |
Kiri, atas, kanan, bawah (mm) |
Pada langkah 4, output(..., 'S') mengembalikan PDF sebagai teks (string), lalu dibungkus response() Laravel. Header inline membuat PDF tampil di tab browser. Jika ingin langsung terunduh, ganti inline menjadi attachment.
View laporan
Buat file resources/views/periode/cetak.blade.php.
Penting: view ini tidak memakai
@extends('layouts.app'), karena layout AdminLTE memakai Bootstrap yang tidak didukung html2pdf. Tata letak disusun dengan<table>dan CSS sederhana.
<style>
.kop td { vertical-align: middle; }
.judul-instansi { font-size: 16pt; font-weight: bold; }
.data { width: 100%; border-collapse: collapse; }
.data th { background-color: #0b6e69; color: #ffffff; padding: 2mm; border: 0.2mm solid #0b6e69; }
.data td { padding: 2mm; border: 0.2mm solid #999999; }
.tengah { text-align: center; }
.aktif { color: #1a7f37; font-weight: bold; }
</style>
<page backtop="32mm" backbottom="14mm" backleft="0mm" backright="0mm">
<page_header>
<table class="kop" style="width: 100%; border-bottom: 0.6mm solid #000000;">
<tr>
<td style="width: 15%;">
<img src="{{ public_path('dist/assets/img/AdminLTELogo.png') }}" style="width: 20mm;">
</td>
<td style="width: 85%;">
<span class="judul-instansi">LABORATORIUM KOMPUTER</span><br>
Sistem Informasi Praktikum (SIMTIKUM)<br>
Jl. Contoh No. 1, Banjarbaru
</td>
</tr>
</table>
</page_header>
<page_footer>
<table style="width: 100%; font-size: 9pt; color: #666666;">
<tr>
<td style="width: 50%;">Dicetak: {{ now()->format('d-m-Y H:i') }}</td>
<td style="width: 50%; text-align: right;">Halaman [[page_cu]] dari [[page_nb]]</td>
</tr>
</table>
</page_footer>
<h3 class="tengah">LAPORAN DATA PERIODE</h3>
<table class="data">
<thead>
<tr>
<th style="width: 8%;">No</th>
<th style="width: 47%;">Periode</th>
<th style="width: 20%;">Singkatan</th>
<th style="width: 25%;">Status</th>
</tr>
</thead>
<tbody>
@foreach ($semua_periode as $periode)
<tr>
<td class="tengah">{{ $loop->iteration }}</td>
<td>{{ $periode->periode }}</td>
<td class="tengah">{{ $periode->singkatan }}</td>
<td class="tengah">
@if ($periode->status)
<span class="aktif">Aktif</span>
@else
Tidak aktif
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
<br>
<table style="width: 100%;">
<tr>
<td style="width: 60%;">
<qrcode value="{{ route('periode.index') }}" ec="M" style="width: 25mm; border: none;"></qrcode>
</td>
<td style="width: 40%;">
Banjarbaru, {{ now()->translatedFormat('d F Y') }}<br>
Koordinator Laboratorium,<br><br><br><br>
<b>Nama Koordinator</b>
</td>
</tr>
</table>
</page>
Bagian-bagian khusus html2pdf:
| Tag / kode | Fungsi |
|---|---|
<page> |
Satu “dokumen halaman”. backtop dan backbottom memberi ruang untuk kop dan footer agar tidak tertimpa isi |
<page_header> |
Kop yang otomatis diulang di setiap halaman |
<page_footer> |
Footer yang otomatis diulang di setiap halaman |
[[page_cu]] / [[page_nb]] |
Nomor halaman saat ini / jumlah seluruh halaman |
<thead> |
Judul kolom tabel otomatis diulang jika tabel berlanjut ke halaman berikutnya |
<qrcode value="..."> |
QR code; ec adalah tingkat koreksi kesalahan (L, M, Q, H) |
public_path(...) |
Gambar memakai path file di komputer, bukan asset() (URL) |
Catatan tentang lebar kolom: beri width dalam persen pada setiap kolom judul (<th>) dan pastikan totalnya 100%. Tanpa itu, html2pdf menyesuaikan lebar kolom dengan isinya sehingga tabel tidak selebar halaman.
Tanggal pada tanda tangan memakai translatedFormat, sehingga nama bulan mengikuti bahasa aplikasi. Agar tampil dalam Bahasa Indonesia (misalnya November, Agustus), ubah APP_LOCALE=id di file .env.
Tombol cetak di halaman index
Buka resources/views/periode/index.blade.php. Di bagian card header, tambahkan tombol Cetak PDF di samping tombol Tambah Data:
<div class="d-flex flex-wrap justify-content-md-end gap-2">
<a href="{{ route('laporan.periode') }}" target="_blank" class="btn btn-sm btn-danger">
<i class="bi bi-file-earmark-pdf me-1" aria-hidden="true"></i>
Cetak PDF
</a>
<a href="{{ route('periode.create') }}" class="btn btn-sm btn-success">
<i class="bi bi-plus-circle me-1" aria-hidden="true"></i>
Tambah Data
</a>
</div>
target="_blank" membuka PDF di tab baru, sehingga halaman aplikasi tetap terbuka.
Menu Laporan di sidebar
Buka resources/views/layouts/components/sidebar.blade.php, tambahkan menu Laporan di bawah menu Data Master:
<li class="nav-header">LAPORAN</li>
<li class="nav-item">
<a href="{{ route('laporan.periode') }}" target="_blank" class="nav-link">
<i class="nav-icon bi bi-file-earmark-pdf"></i>
<p>Laporan Periode</p>
</a>
</li>
nav-header adalah judul kelompok menu pada AdminLTE. Menu ini tidak diberi penanda aktif karena laporan selalu dibuka di tab baru.
Uji coba
- Buka
/periode, klik Cetak PDF. PDF tampil di tab baru dengan kop, tabel, tanda tangan, dan QR code. - Tambahkan beberapa data periode (minimal 30) lewat seeder atau form, lalu cetak ulang. Pastikan:
- kop dan judul kolom tabel muncul di setiap halaman;
- footer menunjukkan "Halaman 1 dari 2", "Halaman 2 dari 2", dan seterusnya.
- Pindai QR code dengan HP, pastikan mengarah ke halaman periode.
- Ganti
inlinemenjadiattachmentdi controller, pastikan PDF terunduh.
Jika terjadi error
| Gejala | Penyebab dan solusi |
|---|---|
Class "Spipu\Html2Pdf\Html2Pdf" not found |
Belum composer require spipu/html2pdf, atau lupa use di controller |
Error tentang gd atau imagecreate |
Ekstensi gd belum aktif; lihat bagian Persiapan |
Html2PdfException ... tag ... is not closed |
Ada tag HTML yang tidak ditutup di view laporan |
| Gambar tidak tampil | Pakai public_path(...), bukan asset(...), dan pastikan file benar-benar ada |
| Tabel tidak selebar halaman | Beri width dalam persen pada setiap <th> dengan total 100% |
Latihan
- Ubah laporan menjadi landscape (mendatar).
- Tambahkan filter agar URL
/laporan/periode?status=1hanya mencetak periode yang aktif. Petunjuk: gunakanrequest('status')dan->when(...)pada query. - Tampilkan jumlah total periode di bawah tabel, misalnya Jumlah: 4 periode.
Rangkuman
- spipu/html2pdf mengubah HTML menjadi PDF dengan murni PHP, tanpa program tambahan di server.
<page_header>,<page_footer>,[[page_cu]], dan[[page_nb]]memudahkan kop dan nomor halaman.- View laporan disusun dengan
<table>dan CSS sederhana, tanpa layout Bootstrap. - Controller merender view menjadi HTML, menuliskannya ke PDF, lalu mengirimnya dengan
response().
Komentar
Login dengan akun GitHub untuk berkomentar atau bertanya.