Zero code print forms for Eloquent
Tags: laravel, php
Published: 2020-11-18 07:01:54

Recently, in a project on Laravel+Eloquent needed to make print forms of documents — invoices, contracts in Word format. Since there are a lot of different documents in the system, I decided to make it universal so that it can be used in other projects later.

The result is an implementation that requires minimal integration costs into the project.

How I used to make print forms. I used different approaches
- I was placing tags in the document template and replacing them during generation.
- Was generating the document text from scratch.
- Was generating html and converting it to word.
- I was creating my own system for generating documents similar to the one described in this article, but using other technologies.

Each option is good for its task. But if you have Eloquent, I highly recommend the way described in this article.

So, we connect the package

```bash
composer require mnvx/eloquent-print-form
```

And we describe Eloquent models, if they are not already described. Let’s say there are the following models.

```php
use Illuminate\Database\Eloquent\Model;

/**
 * @property string $number
 * @property string $start_at
 * @property Customer $customer
 * @property ContractAppendix[] $appendixes
 */
class Contract extends Model
{
    public function customer()
    {
        return $this->belongsTo(Customer::class);
    }

    public function appendixes()
    {
        return $this->hasMany(ContractAppendix::class);
    }
}

/**
 * @property string $name
 * @property CustomerCategory $category
 */
class Customer extends Model
{
    public function category()
    {
        return $this->belongsTo(CustomerCategory::class);
    }
}

/**
 * @property string $number
 * @property string $date
 * @property float $tax
 */
class ContractAppendix extends Model
{
    public function getTaxAttribute()
    {
        $tax = 0;
        foreach ($this->items as $item) {
            $tax += $item->total_amount * (1 - 100 / 
                (100+($item->taxStatus->vat_rate ?? 0)));
        }
        return $tax;
    }
}
```

For clarity, the diagram of the relationships

![Class diagram](https://dev-to-uploads.s3.amazonaws.com/i/7or0j6akg9l5gfn887bv.png)

In other words, we described the table with contracts (`Contract`), the contract can have a client (`Customer`) filled in, and the client can have a category filled in. A contract can have several appendices (`ContractAppendix`).

All you need to generate a print form is to describe the fields in the print form template. Creating a docx file with the following content

![Document example](https://dev-to-uploads.s3.amazonaws.com/i/6kxm0esobd2zmw259qo5.png)

In variables, specify the names of Eloquent entity fields. If you need to get to neighboring tables via links, use a dot, as in the example above, in `${customer.category.name}`.

If you need to process data from the database, use the pipeline operator `|`, as in the example `${number|placeholder}`. If you need to perform multiple processes, build a chain of pipelines, for example `${start_at|date|placeholder}`.

Examples of ready-made operations
- `placeholder` — replaces an empty value with “`____`”,
- `date` — sets the date to the format `24.12.2020`,
- `dateTime` — sets the date-time to the format `24.12.2020 23:11`,
- `int` — converts an integer to the format `2'145`,
- `decimal` — converts a fractional number to the format `2'145.07`.

To fill in table data, insert variables into the table as in the document example above. You can use a separate construction to number table rows `${entities.#row_number}`.

Now that the document is described, you just need to start generating the printed form

```php
use Mnvx\EloquentPrintForm\PrintFormProcessor;
$entity = Contract::find($id);
$printFormProcessor = new PrintFormProcessor();
$templateFile = resource_path('path/your_print_form.docx');
$tempFileName = $printFormProcessor
    ->process($templateFile, $entity);
```

The generated `$tempFileName` file will contain the prepared print form.

In a Laravel project, the controller method responsible for generating the printed form may look like this

```php
public function downloadPrintForm(FormRequest $request)
{
    $id = $request->get('id');
    $entity = Contract::find($id);
    $printFormProcessor = new PrintFormProcessor();
    $templateFile = resource_path(
        'path_to_print_forms/your_print_form.docx');
    $tempFileName = $printFormProcessor
        ->process($templateFile, $entity);
    $filename = 'contract_' . $id;
    return response()
        ->download($tempFileName, $filename . '.docx')
        ->deleteFileAfterSend();
}
```

In summary, I will say that I decently unloaded myself by making this small library for a project where there are quite a lot of print forms. I don’t need to write my own unique code for each of them. I just describe the variables in the documents as mentioned above and get the result very quickly. I will be glad if the package will help you save time.

[Link to the project on github](https://github.com/mnvx/eloquent-print-form).