PDF Exporter

Version Date Notes By
0.1 2017-05-19 Initial release ROB

Creating a basic listing PDF

The PDF Exporter comes with an extended class that abstracts the creation of a listing PDF. This Class uses a blade template that has already been defined so that you do not need to create one to use the PDFListingExporter (we will see further ahead how to use blade templates to create more complex PDFs). To use the listing PDF Exporter just import the class:

use WeMake\Services\Core\PDFListingExporter;

Then create a new instance of this object (or instance it by adding it as an expected parameter in a controller function for example). The second file you will need to create is an ExportTransformer for that Laravel Model (for this example we will use the FamilyExportTransformer in the Documentation Module):

<?php

namespace WeMake\Transformers\Documents;

use Illuminate\Support\Collection;
use Illuminate\Database\Eloquent\Model;
use League\Fractal\TransformerAbstract;
use WeMake\Contracts\Core\CollectionTransformerContract;

/**
 * Class FamilyExportTransformer
 *
 * @package WeMake\Transformers\Documents
 */
class FamilyExportTransformer extends TransformerAbstract implements CollectionTransformerContract
{
    /**
     * Turn this item object into a generic array
     *
     * @param Collection $collection
     *
     * @return array
     */
    public function transform(Collection $collection)
    {
        $records = [];

        foreach ($collection as $record) {
            $records[] = $this->transformRecord($record);
        }

        return $records;
    }

    /**
     * Turn this item object into a generic array
     *
     * @param Model $model
     *
     * @return array
     */
    private function transformRecord(Model $model)
    {
        return [/* Return here an associative array with the field names as keys and the values as values */
            'name' => $model->name,
            'type' => $model->type,
            ...
        ]
    }
}

This export transformer must also be instanced before it can be used. Now we can use this transformer together with the exporter and any laravel model collection (such as the ones returned by eloquent queries) and generate a PDF listing all the models in that list, for example (using the document families again):

// Get the eloquent model collection
$families = Family::all();

// transform the collection using an ExportTransformer
$records = $transformer->transform($families);

// this exporter must be instanced inside one of a controller's methods
// it will generate an appropriate file downloading response
return $exporter
    // set the title that will appear on the PDF's header (header repeats in every page)
    ->setTitle(trans('form.title.listing') . ' ' . trans('page.breadcrumbs.families'))

    // set the orientation to landscape
    ->landscape()

    // add a listing header (first parameter is field key returned by ExportTransformer and second field is name of the header)
    // repeat for each desired header
    ->addListingHeader('name', trans('form.field.name'))
    ->addListingHeader('type', trans('form.field.type'))
    ...

    // download starts the generation process and returns the HTTP response so that the 
    // user's browser begins the download
    ->download($request, $records);

Custom PDF Reports

Base PDFExporter explained

Sometimes more customized reports will be required that cannot be created with the more generic PDF exporters (like the PDFListingExporter). In these cases you will have to use the base PDFExporter and you will have to create a blade template that will be consumed by wkhtmltopdf to generate the pdf.

The first step will be to create the blade template that will serve as the source of the PDF's body (the header and footer are standard and cannot be changed). Refer to Laravel Blade Template for documentation on how to create these templates.

Once you have created the template you will need to instance a new PDFExporter and set the options desired:

$exporter = new PDFExporter();

$exporter
    // this option activates the debug outputs for wkhtmltopdf and saves a rendered version of
    // your template in /backend/storage/pdf_debug/test.html
    // NOTE: do NOT leave this on in production environments as it severely reduces the generation speed
    ->debug()

    // Set title of the document (will appear in the header, header is repeated for every page).
    ->setTitle($title)

    // set the filename of the PDF 
    // (used during store, not important for download because the frontend will change the name)
    ->setFileName($file_name)

    // Sets the layout of the pages to landscape (applies to all pages)
    ->landscape()

    // Sets the layout of the pages to portrait (applies to all pages)
    ->portrait()

    // This function activates the javascript processing functionality of wkhtmltopdf and 
    // imports the echarts library, allowing the use of echart graphics in the PDF report
    ->useECharts()

    // this function accepts a request, the dot notation name for the body blade template
    // and an associative array of variables for the body data (data made available to the
    // body template) and generates the PDF, sending it to the requesting browser.
    // NOTE: This function cannot be used at the same time as the baseStore
    ->baseDownload($request, $body, $bodyData)

    // Same as the baseDownload function but instead of sending it to the requesting browser
    // it stores it as a file in the server (don't forget to associate it with a model that
    // has the trait hasFiles)
    ->baseStore($request, $body, $bodyData)

Some concerns constructing the blade template

Sometimes it will be required to force the template to "break" the page at a specific point and continue from that point on in a new page. This can be done like so:

<!--Content in old page-->
<div class="page-breaker"></div> <!-- This forces a new page to start from this point on -->
<!--Content in new page ☺-->

Using ECharts in the backend

This functionality is new and subject to change

Sometimes you will need to add graphics to the PDF report you are generating. To do this you will need to call the useECharts() in the exporter object otherwise the charts will not be rendered. Then, in your blade template, you will need to add the following:

@include('core.pdf.fragments.echart-object', [
    'name' => 'delay_evaluation', // name of the chart - must be unique in the template
    'title' => trans('report.text.cost-evaluation'), // title of the chart - appears at the top
    'global_type' => 'bar', // global chart type, refer to ECharts documentation for possible types
    'size' => ['x' => 400, 'y' => 400], // size, must be in pt (relative mesurements not accepted)
    'xaxis' => [ // configurations for xaxis
        // data stores the name of each element of the xaxis (if they are names and not numbers)
        'data' => [trans('report.text.plan-for') . ' ' . $plan->name]
    ],
    // series stores an array of associative arrays, each associative array defines one series
    // review the ECharts documentation for the definition of series and what it can do
    'series' => [
        [
            'name' => trans('report.text.savings'), // name of the series
            'stack' => 'stack_1', // stack (optional) - defines if this bar, line, ... stacks on top of other series with the same stack name
            'data' => [$plan->cost_saved], // array of values for this serie
            'itemStyle' => [ // defines the style of the serie (optional)
                'color' => '#388E3C' // override default color for this series
            ]
        ],

        ...
    ]
])

This creates a chart with the defined caracteristics at the place it was called, for example:

<div class="graphic-container">
    @include('core.pdf.fragments.echart-object', [
        ...
    ])
</div>

will create a graphic inside the div with the class graphic-container.