Недавно в проекте на Laravel+Eloquent понадобилось сделать печатные формы документов — счетов, договоров в формате Word. Так как в системе много разных документов, то решил сделать универсально, чтобы можно было потом использовать и в других проектах.
В итоге получилась реализация, которая требует минимум затрат на интеграцию в проект.
Как я раньше делал печатные формы. Использовал разные подходы
- В шаблон документа размещал теги и заменял их при генерации.
- Генерировал текст документа с нуля.
- Генерировал html и конвертировал его в word.
- Делал свою систему для генерации документов по аналогии с той, что описана в этой статье, но на других технологиях.
Каждый вариант хорош для своей задачи. Но если у вас Eloquent, то весьма рекомендую вариант, описанный в этой статье.
Итак, подключаем пакет
composer require mnvx/eloquent-print-form
И описываем модели Eloquent, если они еще не описаны. Допустим, есть следующие модели.
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;
}
}
Для наглядности, диаграмма связей
То есть описана таблица с договорами (Contract
), у договора может быть заполнен контрагент (Customer
), у контрагента может быть заполнена категория. У договора может быть несколько приложений (ContractAppendix
).
Все что нужно для генерации печатной формы — описать поля в шаблоне печатной формы. Создаем docx файл с таким содержимым
В переменных указываем названия полей сущностей Eloquent. Если нужно добраться по связям до соседних таблиц, используем точку, как в примере выше, в ${customer.category.name}
.
Если необходимо обработать данные из базы, используем оператор конвейер |
, как в примере ${number|placeholder}
. Если нужно выполнить несколько обработок, строим цепочку конвейеров, например ${start_at|date|placeholder}
.
Примеры готовых операций
placeholder
— заменяет пустое значение на "____",date
— приводит дату к формату 24.12.2020,dateTime
— приводит дату-время к формату 24.12.2020 23:11,int
— приводит целое число к формату 2`145,decimal
— приводит дробное число к формату 2`145.07.
Для заполнения табличных данных вставляем переменные в таблицу как в примере документа выше. Для нумерации строк таблицы можно использовать отдельную конструкцию ${entities.#row_number}
.
Теперь, когда документ описан, остается просто запустить генерацию печатной формы
use Mnvx\EloquentPrintForm\PrintFormProcessor;
$entity = Contract::find($id);
$printFormProcessor = new PrintFormProcessor();
$templateFile = resource_path('path_to_print_forms/your_print_form.docx');
$tempFileName = $printFormProcessor->process($templateFile, $entity);
В сгенерированном файле $tempFileName
будет лежать подготовленная печатная форма.
В проекте на Laravel метод контроллера, отвечающий за генерацию печатной формы, может выглядеть так
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();
}
Резюмируя, скажу, что я прилично разгрузил себя, сделав эту небольшую библиотеку в проекте, где довольно много печатных форм. Мне не надо для каждой из них писать свой уникальный код. Я просто описываю переменные как сказано выше и весьма быстро получаю результат. Буду рад, если и вам пакет поможет сэкономить время.
roxblnfk
Вы пробовали tinybutstrong с плагином opentbs?
С помощью этого старичка можно генерировать достаточно сложные офисные документы.
Блоки (таблицы, списки или любая штука для выражения массива) можно генерить с глубокой вложенностью; повторения можно делать даже постранично (по странице на каждый элемент массива); можно вставлять картинки. Также есть и форматирование значений (даты, числа...) и выражения с условиями. Много всего, в общем.
Один недостаток — всё-таки это легаси. Может только по этому на хабре ещё нет статьи про него
mnv Автор
Интересный проект. Мне он не попадался. Похож на легаси, но все равно удивительно что за столько лет проект не набрал популярность сравнимую с phpoffice при такой функциональности.