(Note: This article has been updated with Breeze starter kit in place of Jetstream.)
In this tutorial we’ll use dompdf package to generate PDF within a Laravel 8 app using VILT (Vue.js, Inertia.js, Lavarel, Tailwind) stack. Sometimes it becomes tricky to get PDF output from Inertia.js based Laravel app. In this case the key is to avoid inertia.js in request-response cycle i.e. on form submit or defining links or return from controller. Following app will demonstrate this point.
Assuming that the composer is installed, enter the following command to create new Laravel 8 project:
composer create-project laravel/laravel app 8.*
Now go inside the newly created app folder
cd app
Enter following command to add Breeze package:
composer require laravel/breeze:1.9.2
Now install Breeze with Vue and Inertia capability by entering following command:
php artisan breeze:install vue
Assuming node.js and npm are installed, enter the following command to install required node modules:
npm install
Next, setup database and Laravel environment file as usual. Here, I’m configuring SQLite database for simplicity purposes:
touch database/database.sqlite
Edit .env file as follows:
DB_CONNECTION = sqlite
You can remove DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME, DB_PASSWORD from .env when using SQLite database.
Enter following command to run the migrations:
php artisan migrate
For generating PDFs we’ll use barryvdh/laravel-dompdf package. Install this package by executing following command:
composer require barryvdh/laravel-dompdf
Since, initial setup of Laravel app is complete, we can insert data in the database. Here we’ll take shortcut by inserting data through tinker in the users table. First enter the following command to start tinker:
php artisan tinker
Then enter the following command in order to generate 10 random users:
User::factory()->count(10)->create()
Type ‘exit’ and press Enter to leave the tinker shell. Now, we move onto Controller part by creating new UserController as follows:
php artisan make:controller UserController
This command will create app/Http/Controllers/UserController.php. Go ahead and open this file and change it as follows:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\User;
use Inertia\Inertia;
class UserController extends Controller
{
public function index(Request $request)
{
return Inertia::render('Users/Index', [
'users' => User::all()
]);
}
public function report(Request $request)
{
$param = $request->input('param');
$users = User::all();
$pdf = app('dompdf.wrapper');
$pdf->getDomPDF()->set_option("enable_php", true);
$pdf->loadView('report', compact('users','param'));
return $pdf->stream('users.pdf');
}
}
Now, create a new folder named ‘Users‘ in resources/js/Pages/ (be aware of capitalization). Create Index.vue inside ‘Users‘ folder (again watch out capitalization).
Insert following code in resources/js/Pages/Users/Index.vue:
<template>
<div class="bg-gray-100 shadow mx-auto lg:max-w-4xl">
<form @submit.prevent="submit" action="report" ref="form">
<div class="p-5">
<label for="param">Enter your name:</label>
<input type='text' name="param" class="px-2 ml-2 rounded-lg border">
<button type="submit" class="px-2 py-1 ml-2 rounded-lg border bg-gray-500 text-white hover:bg-black">Generate PDF</button>
</div>
</form>
<table class="w-full">
<tr class="bg-gray-900 text-white">
<th class="px-2 py-1 text-sm font-bold text-left">ID</th>
<th class="px-2 py-1 text-sm font-bold text-left">Name</th>
<th class="px-2 py-1 text-sm font-bold text-left">Email</th>
</tr>
<tr v-for="(user, index) in users" :key="index" :class="{'bg-gray-300': index%2===0}">
<td class="px-2 py-1 text-sm text-left">{{user.id}}</td>
<td class="px-2 py-1 text-sm text-left">{{user.name}}</td>
<td class="px-2 py-1 text-sm text-left">{{user.email}}</td>
</tr>
</table>
</div>
</template>
<script>
export default {
props: {
users:Object
},
methods:{
submit: function(){
this.$refs.form.submit()
},
},
}
</script>
By employing Vue’s ‘ref‘ functionality we used simple form submission method call to submit form without involving Inertia, so that weird errors could be avoided while using dompdf.
Now, for the actual PDF file generation, we’ll use classic blade template. So, create a new file resources/views/report.blade.php and insert the following code in this file:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Users Report</title>
</head>
<body>
<div style="margin-bottom:25px;"><strong>Report generated by: {{$param}}</strong></div>
<table style="border-collapse: collapse;">
<thead>
<tr>
<th style="border-bottom:2pt solid black;">
<strong>ID</strong>
</th>
<th style="border-bottom:2pt solid black;">
<strong>Name</strong>
</th>
<th style="border-bottom:2pt solid black;">
<strong>Email</strong>
</th>
</tr>
</thead>
<tbody>
@foreach ($users as $user)
<tr>
<td style="border-bottom:1pt solid black;">
{{$user->id}}
</td>
<td style="border-bottom:1pt solid black;">
{{$user->name}}
</td>
<td style="border-bottom:1pt solid black;">
{{$user->email}}
</td>
</tr>
@endforeach
</tbody>
</table>
</body>
</html>
Finally, open routes/web.php and put the following line at the top of it:
use App\Http\Controllers\UserController;
And then insert following route into this web.php:
Route::get('users', [UserController::class, 'index'])->name('users');
Route::get('report', [UserController::class, 'report'])
->name('report');
Execute the following command to compile UI assets:
npm run dev
Run Laravel development server by executing:
php artisan serve
Enter the following URL in your browser to access index of Users:
localhost:8000/users
Now, enter your name in the input field and click ‘Generate PDF‘ button.