PDF Generation and Download in Laravel-Vue By Snappy
In this guide, we'll explore how to implement a robust PDF generation and download system using Laravel for the backend and Vue.js for the frontend....
Gurpreet Kait
Author
In this guide, we'll explore how to implement a robust PDF generation and download system using Laravel for the backend and Vue.js for the frontend....
Gurpreet Kait
Author
In this guide, we'll explore how to implement a robust PDF generation and download system using Laravel for the backend and Vue.js for the frontend. We'll specifically focus on generating purchase order PDFs using the wkhtmltopdf library through Laravel Snappy.
First, install the Laravel Snappy package
composer require barryvdh/laravel-snappy
I'm assuming that you are using a Linux OS, In ubuntu I used which wkhtmltopdf
to see the binary path.
Configure your config/snappy.php
return [
'pdf' => [
'enabled' => true,
'binary' => '/usr/bin/wkhtmltopdf',
'timeout' => 120,
'options' => [
'enable-local-file-access' => true,
'encoding' => 'UTF-8',
'page-size' => 'A4'
],
]
];
Create a blade template for your PDF (resources/views/pdf_templates/purchase_order.blade.php
)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Purchase Order</title>
<style>
/* Inline styles for better PDF rendering */
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
}
/* ... more styles ... */
</style>
</head>
<body>
<!-- Your PDF content structure -->
<div class="header">
<h1>Purchase Order #{{ $purchaseOrder->po_number }}</h1>
</div>
<!-- ... more content ... -->
</body>
</html>
Create your controller methods:
class PurchaseOrderController extends Controller
{
public function downloadInvoiceSnappyPdf(PurchaseOrder $purchaseOrder)
{
try {
$pdf = SnappyPdf::loadView('pdf_templates.purchase_order', [
'purchaseOrder' => $purchaseOrder
])
->setOption('encoding', 'UTF-8')
->setOption('margin-top', 10);
return $pdf->download($purchaseOrder->po_number . '.pdf');
} catch (\Exception $e) {
return response()->json(['error' => 'Failed to generate PDF'], 500);
}
}
}
Vue Service ( In short you need to make an Api Call, In my case I'm using service files for example: PurchaseOrderService.js, InvoiceService.js)
Create a service to handle API calls:
// services/PurchaseOrderService.js
export const exportPurchaseOrderInvoice = async (id) => {
const response = await axios.get(`/api/purchase-orders/${id}/invoice/pdf`, {
responseType: 'blob'
});
return response.data;
};
Implement the download functionality in your component: (You can point this to click event)
// components/PurchaseOrder.vue
export default {
methods: {
async downloadPdf() {
try {
const response = await exportPurchaseOrderInvoice(this.getSelectedItem.id);
const blob = new Blob([response], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `${this.getSelectedItem.po_number}.pdf`;
link.click();
URL.revokeObjectURL(url);
} catch {
this.$notification.$emit('error', 'Failed to download PDF');
}
}
}
}
For better code organization and reusability, we can create a composable:
Introduction To Composition Api For Newbie
// composables/useDownloader.js
import { ref } from 'vue';
export function useDownloader() {
const isDownloading = ref(false);
const downloadFile = async (getData, filename) => {
isDownloading.value = true;
try {
const response = await getData();
const url = URL.createObjectURL(new Blob([response]));
const link = document.createElement('a');
link.href = url;
link.download = filename;
link.click();
URL.revokeObjectURL(url);
} finally {
isDownloading.value = false;
}
};
return { downloadFile, isDownloading };
}
It's more likely creating a hook or a helper function in Laravel.
// TopButtons.vue
import { useDownloader } from '@/composables/useDownloader';
import { exportPurchaseOrderInvoice } from '@/services/PurchaseOrderService';
export default {
name: 'TopButtons',
props: {
showBuilder: Boolean,
loading: Boolean,
instance: Object
},
setup(props) {
const { downloadFile, isDownloading } = useDownloader();
// Method to handle PDF download
const downloadPdf = async () => {
try {
await downloadFile(
// Pass the function that gets the data
() => exportPurchaseOrderInvoice(props.instance.getSelectedItem.id),
// Set the filename using PO number
`${props.instance.getSelectedItem.po_number}.pdf`
);
} catch (error) {
// Handle any errors
console.error('PDF download failed:', error);
}
};
return {
downloadPdf,
isDownloading
};
},
// In template
template: `
<div>
<v-btn
color="#F3F4FA"
:loading="isDownloading"
@click="downloadPdf"
>
<v-icon size="22">
{{ mdiPdfBox }}
</v-icon>
</v-btn>
</div>
`
});
If you encounter issues with wkhtmltopdf, ensure proper installation:
Happy coding! 🚀