AI
/laravel-agent:pdf:make
Create PDF generation feature using spatie/laravel-pdf or barryvdh/laravel-dompdf
Overview
The /pdf:make command generates complete PDF generation features with templates, controllers, and views. It supports both modern spatie/laravel-pdf (Browsershot-based with CSS Grid/Flexbox support) and the simpler barryvdh/laravel-dompdf package.
Usage
/laravel-agent:pdf:make [PDF Type] [options]
Examples
# Create an invoice PDF generator
/laravel-agent:pdf:make Invoice
# Create a report with DomPDF
/laravel-agent:pdf:make Report --package=dompdf
# Create a certificate PDF
/laravel-agent:pdf:make Certificate
# Create a contract PDF
/laravel-agent:pdf:make Contract
What Gets Created
A complete PDF generation feature includes the following components:
| Component | Location | Description |
|---|---|---|
| PDF Generator Class | app/Pdf/ |
Main PDF generator with download, stream, and save methods |
| PDF Controller | app/Http/Controllers/ |
Controller with download and stream endpoints |
| Main Template | resources/views/pdf/[type]/template.blade.php |
Primary PDF layout and content |
| Header Template | resources/views/pdf/[type]/header.blade.php |
Optional PDF header (Spatie only) |
| Footer Template | resources/views/pdf/[type]/footer.blade.php |
Optional PDF footer (Spatie only) |
| Routes | routes/web.php |
Download and view routes |
| Tests | tests/Feature/ |
Feature tests for PDF generation |
Package Options
The command supports two PDF generation packages:
Spatie Laravel PDF (Default)
- Modern - Uses Chrome/Chromium for rendering via Browsershot
- CSS Support - Full CSS Grid, Flexbox, and modern layout support
- Headers/Footers - Separate header and footer templates
- High Quality - Best for complex layouts and modern designs
Barryvdh DomPDF
- Simple - No external dependencies, pure PHP
- Lightweight - No Chromium installation required
- Basic CSS - Limited CSS support, best for simple layouts
- Easy Deploy - Works on any shared hosting
Example Output Structure
For /laravel-agent:pdf:make Invoice:
app/
├── Pdf/
│ └── InvoicePdf.php
└── Http/
└── Controllers/
└── InvoicePdfController.php
resources/views/pdf/
└── invoice/
├── template.blade.php
├── header.blade.php
└── footer.blade.php
routes/
└── web.php (updated)
tests/Feature/
└── InvoicePdfTest.php
Generated PDF Class (Spatie)
<?php
declare(strict_types=1);
namespace App\Pdf;
use App\Models\Invoice;
use Spatie\LaravelPdf\Facades\Pdf;
use Spatie\LaravelPdf\PdfBuilder;
final class InvoicePdf
{
public function __construct(
private readonly Invoice $invoice,
) {}
public function generate(): PdfBuilder
{
return Pdf::view('pdf.invoice.template', [
'invoice' => $this->invoice,
'company' => config('app.company'),
])
->format('a4')
->headerView('pdf.invoice.header', ['invoice' => $this->invoice])
->footerView('pdf.invoice.footer');
}
public function download(): \Symfony\Component\HttpFoundation\Response
{
return $this->generate()
->name("invoice-{$this->invoice->number}.pdf")
->download();
}
public function save(string $path): void
{
$this->generate()->save($path);
}
public function stream(): \Symfony\Component\HttpFoundation\Response
{
return $this->generate()
->name("invoice-{$this->invoice->number}.pdf")
->inline();
}
}
Generated PDF Class (DomPDF)
<?php
declare(strict_types=1);
namespace App\Pdf;
use App\Models\Invoice;
use Barryvdh\DomPDF\Facade\Pdf;
final class InvoicePdf
{
public function __construct(
private readonly Invoice $invoice,
) {}
public function generate(): \Barryvdh\DomPDF\PDF
{
return Pdf::loadView('pdf.invoice.template', [
'invoice' => $this->invoice,
'company' => config('app.company'),
])
->setPaper('a4', 'portrait')
->setOptions([
'isHtml5ParserEnabled' => true,
'isRemoteEnabled' => true,
]);
}
public function download(): \Symfony\Component\HttpFoundation\Response
{
return $this->generate()->download("invoice-{$this->invoice->number}.pdf");
}
public function stream(): \Symfony\Component\HttpFoundation\Response
{
return $this->generate()->stream("invoice-{$this->invoice->number}.pdf");
}
public function save(string $path): void
{
$this->generate()->save($path);
}
}
Generated Controller
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Models\Invoice;
use App\Pdf\InvoicePdf;
use Illuminate\Http\Request;
final class InvoicePdfController extends Controller
{
public function download(Invoice $invoice)
{
$this->authorize('view', $invoice);
return (new InvoicePdf($invoice))->download();
}
public function stream(Invoice $invoice)
{
$this->authorize('view', $invoice);
return (new InvoicePdf($invoice))->stream();
}
}
Generated Blade Template
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Invoice #</title>
<style>
* { font-family: 'DejaVu Sans', sans-serif; }
body { font-size: 12px; line-height: 1.4; color: #333; }
.header { border-bottom: 2px solid #333; padding-bottom: 20px; margin-bottom: 20px; }
.company-name { font-size: 24px; font-weight: bold; }
table { width: 100%; border-collapse: collapse; }
th, td { padding: 10px; text-align: left; border-bottom: 1px solid #ddd; }
th { background: #f5f5f5; }
.total-row { font-weight: bold; font-size: 14px; }
.text-right { text-align: right; }
.footer { margin-top: 40px; font-size: 10px; color: #666; text-align: center; }
</style>
</head>
<body>
<div class="header">
<div class="company-name"></div>
<div></div>
</div>
<h1>Invoice #</h1>
<p><strong>Date:</strong> </p>
<p><strong>Due:</strong> </p>
<h2>Bill To</h2>
<p><br></p>
<table>
<thead>
<tr>
<th>Description</th>
<th class="text-right">Qty</th>
<th class="text-right">Price</th>
<th class="text-right">Total</th>
</tr>
</thead>
<tbody>
@foreach($invoice->items as $item)
<tr>
<td></td>
<td class="text-right"></td>
<td class="text-right"></td>
<td class="text-right"></td>
</tr>
@endforeach
<tr class="total-row">
<td colspan="3" class="text-right">Total</td>
<td class="text-right"></td>
</tr>
</tbody>
</table>
<div class="footer">
<p>Thank you for your business!</p>
</div>
</body>
</html>
Generated Routes
<?php
Route::middleware('auth')->group(function () {
Route::get('/invoices/{invoice}/pdf', [InvoicePdfController::class, 'download'])
->name('invoices.pdf.download');
Route::get('/invoices/{invoice}/pdf/view', [InvoicePdfController::class, 'stream'])
->name('invoices.pdf.stream');
});
Usage Examples
<?php
// Download PDF
return (new InvoicePdf($invoice))->download();
// Stream (view in browser)
return (new InvoicePdf($invoice))->stream();
// Save to storage
(new InvoicePdf($invoice))->save(storage_path('app/pdfs/invoice-123.pdf'));
Best Practices
- Choose the right package - Use Spatie for modern layouts, DomPDF for simple documents
- Use web-safe fonts - DejaVu Sans is included with both packages
- Test PDF rendering - Different packages render CSS differently
- Optimize images - Large images increase PDF file size significantly
- Add authorization - Always verify users can access the PDF data
- Consider async generation - Queue large PDFs to avoid timeouts
Common PDF Types
- Invoice - Billing documents with line items and totals
- Report - Analytics, summaries, and data visualizations
- Certificate - Completion certificates, awards, diplomas
- Contract - Legal agreements, terms and conditions
- Receipt - Purchase confirmations and transaction records
- Label - Shipping labels, product labels, badges
Next Steps
- Customize the PDF template in
resources/views/pdf/[type]/ - Add company configuration to
config/app.php - Test with sample data to verify rendering
- Add custom fonts if needed (see package documentation)
- Configure paper size and orientation in the generator class
See Also
- /laravel-agent:mail:make - Create email templates with PDF attachments
- /laravel-agent:resource:make - Create models for PDF data
- /laravel-agent:test:make - Generate PDF generation tests