April 22, 2017

Beautifying Tables when Exporting PDF with Laravel

In many occation, my clients wanted their data to be exported into a PDF file so that it is portable and ready to print whenever the needed to. There are times when we, as a developer, need to generate those data in form of tables. The tricky part is to make tables look nice in a PDF file when exporting data with the help of PHP. In this article I am going to share some experience to make your tables look nice on a export-to-pdf process.

 Suppose that a client has an employees table that he wants to export into a PDF file with table-formatted like this:
Employees table

Step 1 : Add an "Export to PDF" Button

The first thing that we should do, of course, is to add an "Export to PDF" button somewhere on the page. In this case, I put it below the pagination buttons. Open the view file and add these html lines:

<div class="text-center">
    <a href="{{ url('/employees/export-to-pdf') }}" class="btn btn-primary">
        Export to PDF
    </a>
</div>

Notice that the href attribute of the anchor tag (the button) is leading to /employees/export-to-pdf. So later we need to make a route for it. For now, let's just save the view file, and refresh the page in the browser. It should look like this:

An "Export to PDF" button has been added

Step 2 : Install a PDF Library

Next we need to install a tcpdf library that works well with laravel. I'm not recommending any specific package, but since use it in most of my projects, I will be using tcpdf-laravel from elibyy throughout this article.

Open the terminal, enter your laravel project directory, and run this artisan command:

composer require elibyy/tcpdf-laravel

After composer is done installing the package, open up the config/app.php file, and add this line somewhere inside the providers array

Elibyy\TCPDF\ServiceProvider::class,

Also, add this line somewhere inside the aliases array:

'PDF' => Elibyy\TCPDF\Facades\TCPDF::class,

Now save the file and open the routes/web.php file.

Step 3 : Add a Route

Add this line to make a route for our /employees/export-to-pdf url:

Route::get('/employees/export-to-pdf', 'EmployeeController@exportToPdf');
Note that if you don't have the controller, create it before you add the route. This is how you create a controller from the terminal:

php artisan make:controller EmployeeController

Now save the file, and get ready put some code in the controller.

Step 4 : Add a Method in The Controller

Next, open up the app/Http/Controllers/EmployeeController.php file, and add a public method as we defined previously in the route.

public function exportToPdf()
{
    $employees = \App\User::all();
    $html = view('employees-pdf', compact('employees'));

    PDF::SetTitle('Employees Table');
    PDF::SetPrintHeader(false);
    PDF::SetPrintFooter(false);
    PDF::SetMargins(20, 10, 15);
    PDF::AddPage('P','LEGAL');

    PDF::writeHTML($html, true, false, true, false, '');
    $filename = public_path() . '/files/employees.pdf';
    PDF::output($filename,'I');
}

Here is how it works:
  • The first two lines of the method, are to gather all of the employees data, and load it into a view file named resources/views/employees-pdf.blade.php. Then store the view to a variable $html.
  • The next five lines are the PDF settings.
  • The last three lines are to save the view to a pdf file named /files/employees.pdf, relative to the public directory (also relative to the domain name of the application).
  • Note that on the last line, the second parameter 'I' is to directly stream the pdf to the browser. So you will be able to instantly see the PDF as a response of the /employee/esport-to-pdf url.

Step 5 : Create a View File

According to the exportToPdf method on the EmployeeController, There is supposed to be a view file named resource/views/employees-pdf.blade.php. so create the file if it hasn't been created yet. Open the file and put these html code:

<!DOCTYPE html>
<html>
<head>
    <title>Employees</title>
</head>
<body>
        <h1>Employees Table</h1>
        <table class="table table-bordered" cellpadding="5" width:"100%">
            <tr>
                <th><div>ID</div></th>
                <th><div>Name</div></th>
                <th><div>Age</div></th>
                <th><div>Division</div></th>
                <th><div>Salary</div></th>
                <th><div>Performance</div></th>
            </tr>
            @foreach($employees as $employee)
            <tr nobr="true">
                <td>{{ $employee->id }}</td>
                <td>{{ $employee->name }}</td>
                <td>{{ $employee->age }}</td>
                <td>{{ $employee->division }}</td>
                <td>{{ $employee->salary }}</td>
                <td>{{ $employee->performance }}</td>
            </tr>
            @endforeach
        </table>
</body>
</html>

Save it and test the "Export to PDF" button from the browser. It should look something like this:

Messy Employees Table

Okay it works.. But.. It is sooo ugly..
We need to tweak the HTML mockup to beautify the table. Back to the employees-pdf.blade.php file.

Add some styling just above the closing </head> tag

...
<style>
    .text-center{text-align:center;}
    .text-right{text-align:right;}
    td,th{border:1px solid #000000;}
</style>
...

Add a pair of <thead></thead> tag after the first closing </tr> tag, and fill it with these:

...
<thead>
    <tr>
        <th width="50"><div class="text-center">1</div></th>
        <th width="200"><div class="text-center">2</div></th>
        <th width="50"><div class="text-center">3</div></th>
        <th><div class="text-center">4</div></th>
        <th width="70"><div class="text-center">5</div></th>
        <th><div class="text-center">6</div></th>
    </tr>
</thead>
...

The thead pairs will allow anything it contained to be displayed on each and every page.

Next, add an opening <tbody> tag before the @foreach statement, and also a closing </tbody> tag after the @endforeach statement.

...
<tbody>
    @foreach($employees as $employee)
        <tr>
            ...
        </tr>
    @endforeach
</tbody>
...

Next, adjust the width of the column titles, by following the width we defined in thead above

...
<tr>
    <th width="50">ID</th>
    <th width="200">Name</th>
    <th width="50">Age</th>
    <th>Division</th>
    <th width="70">Salary</th>
    <th>Performance</th>
</tr>
...

Lastly, center some columns by applying a corresponding class that we defined in the style tag above. Overall, the final version of the employees-pdf.blade.php file will be like this:

<!DOCTYPE html>
<html>
<head>
    <title>Employees</title>
    <style>
        .text-center{text-align:center;}
        .text-right{text-align:right;}
        td,th{border:1px solid #000000;}
    </style>
</head>
<body>
        <h1>Employees Table</h1>
        <table class="table table-bordered" cellpadding="5" width:"100%">
                <tr>
                    <th width="50"><div class="text-center">ID</div></th>
                    <th width="200"><div class="text-center">Name</div></th>
                    <th width="50"><div class="text-center">Age</div></th>
                    <th><div class="text-center">Division</div></th>
                    <th width="70"><div class="text-center">Salary</div></th>
                    <th><div class="text-center">Performance</div></th>
                </tr>
            <thead>
                <tr>
                    <th width="50"><div class="text-center">1</div></th>
                    <th width="200"><div class="text-center">2</div></th>
                    <th width="50"><div class="text-center">3</div></th>
                    <th><div class="text-center">4</div></th>
                    <th width="70"><div class="text-center">5</div></th>
                    <th><div class="text-center">6</div></th>
                </tr>
            </thead>
            <tbody>
                @foreach($employees as $employee)
                <tr>
                    <td><div class="text-center">{{ $employee->id }}</div></td>
                    <td>{{ $employee->name }}</td>
                    <td><div class="text-center">{{ $employee->age }}</div></td>
                    <td>{{ $employee->division }}</td>
                    <td>
                        <div class="text-right">
                            $ {{ number_format($employee->salary,0,',','.') }}
                        </div>
                    </td>
                    <td>
                        <div class="text-center">
                            {{ $employee->performance }}
                        </div>
                    </td>
                </tr>
                @endforeach
            </tbody>
        </table>
</body>
</html>

Save the file and test it out in the browser. It will look something like this:

Good Looking Table

Better.. But hey, if you look at the bottom at each page, there will be an ugly breaking.

Static Column Title, But Ugly Breaks

Well.. That is eazy.. Just add a nobr="true" attribute on the tr tag under the @foreach statement line

...
<tbody>
    @foreach($employees as $employee)
        <tr nobr="true">
            ...
        </tr>
    @endforeach
</tbody>
...

Save the file again, and test it in the browser. It will look something like this:

Nice Break

Amazing.. We just got ourselves a nice and neat table. Why don't you give it a try and leave a comment below.

Happy coding ^_^

April 18, 2017

Mass-generating QR-Code Coupon with Laravel

Okay, so my client asked me to generate some discount coupons for his loyal customers on hteir next visit to his cafe. The coupons must each have unique QR-code, so that the cashier lady could scan it and automatically reducing the total amount that the customer must pay.

That's eazy.. But..

He asked me to generate 1000 coupon codes! It would pretty sure took days to accomplish using photoshop *_*

Sleek-looking Discount Coupon

So I decided to mass-generat it using laravel. And with some "plugin" packages, of course. Here is how I did it.

Step 1 : Prepare a template for the discount coupon

The template could just be a JPEG or a PNG image file.

A coupon template I got from my client

Copy this file into the storage/app/ directory. Rename it to template.png, or template.jpg depending on original file type.

Step 2 : Install a QR Code Package

Assuming you already have a Laravel application running, get a qrcode package. I used milon/barcode by the way. Run this command on the terminal:

composer require milon/barcode

And wait until the package installation completed successfully. After the installing the qrcode, open up the config/app.php file and add this line Milon\Barcode\BarcodeServiceProvider::class somewhere in the providers array, and also add these two lines 'DNS1D' => Milon\Barcode\Facades\DNS1DFacade::class and 'DNS2D' => Milon\Barcode\Facades\DNS2DFacade::class somewhere inside the aliases array. I would look like this, afterward:

'providers' => [
    ...
    Milon\Barcode\BarcodeServiceProvider::class,
    ...
]
...
'aliases' => [
    ...
    'DNS1D' => Milon\Barcode\Facades\DNS1DFacade::class,
    'DNS2D' => Milon\Barcode\Facades\DNS2DFacade::class,
]

Step 3 : Create the Artisan Command for Generating Coupons

Take the advantage of Laravel's artisan command to mass-generate 1000 discount coupons. But first let's create a command that will do that. Run this command from the terminal:

php artisan make:command GenerateCoupons

This command will create a new file called GenerateCoupons.php inside the app/Console/Command/ directory. Open the file and pour some code into it.

Find a line that looks like this:
protected $signature = 'email:send {user}';
and change it into this
protected $signature = 'generate:coupon {how_many}';

Basicly, what this line provides is that, I will be able to run "generate:coupon" command from the terminal with an argument defining how many discount coupons we want to generate. For example, if I want to generate 1000 discount coupons, I would simply run "generate:coupon 1000" from my terminal.

Awesome...

Next, time to put some real coding here. Find the handle() function and add some serious codes ^_^ :

public function handle()
{
    $count  = $this->argument('how many');
    $img    = \Image::make(storage_path().'/app/template.png');
    for($i=1;$i<=$count;$i++) {
        $qrcode = \Image::make( base64_decode(\DNS2D::getBarcodePNG($base_url.$code, "QRCODE")))->heighten(390);
        $img->insert($qrcode, 'top-left',372,79);
        $img->save(public_path('qr/').$code.'.png',130);
        $this->info($i.' generated');
    }
}
If you have different template file from the one that I have, you can adjust the size of your qr-code, and also adjust the qr-code positioning relative to the template. Just change the number inside the heighten() function to adjust qr-code size (in pixels). Change the second, third, and fourth parameter of the insert() function to adjust the relative position of the qr-code relative to the template. Note that the second parameter could be any of these values:
  • top-left
  • top-right
  • bottom-left
  • bottom-right
This is the illustration how I measured those parameters:
Save this file and, open the next one, app/Console/Kernel.php.

Step 4 : Registering the Command

So we are currently editing the Kernel.php file. Find this line
protected $commands = [
    Commands\SendEmails::class
];
And change it to
protected $commands = [
    Commands\GenerateCoupons::class
];
this will register our GenerateCoupons class so that Laravel's artisan can recognize and able to execute it via the command name.

Step 5 : Moment of Truth

Run this from the terminal:

php artisan generate:coupon 1000

To find out whether the command successfully generating 1000 discount coupons, open up the public/coupons/ folder.

How's that? Pretty cool huh? Why don't you give it a try and leave a comment below