Год назад ко мне в очередной раз обратился постоянный заказчик. «Менеджеры — идиоты, — негодовал он. — Текучка кадров огромная, на обучение каждого уходят месяцы. А потом они делают ошибки».
Лишь после такой эмоциональной прелюдии последовало задание: «Необходимо, чтоб при заказе с сайта на почту приходил уже сформированный пакет документов в форматах MS Office: бланк заказа, счет к оплате, товарный чек, и спецификация».
Задача сама по себе близка к статье «PHP + Word». Но сервис LiveDocx нам не подошел. Во-первых, предполагалось, что большая часть документов должна быть в Excel. Во-вторых, оплата «на Запад» из России была и есть проблематичной. В-третьих, у заказчика была масса идей и хотелок, которые этот, или подобный сервис вряд ли реализовал бы. Например, сложную таблицу, с разбивкой товаров на категории.
Так как сайтов у заказчика несколько, решено было строить отдельную систему — свой собственный сервис генерации документов, к которому сайты обращались бы в момент заказа.
В основу будущего кода легли идеи из той же, приведенной выше, хабростатьи, а так же публикация «Разбираем xlsx в PHP без готовых библиотек».
В итоге за месяц работы «генератор документов» был создан и запущен.
Что он умеет:
- генерировать по шаблону документ docx путем прямой замены шаблонного тега на данные;
- то же самое делать с шаблонами в формате xlsx;
- расширять таблицы в xlsx и заполнять их входящими данными из массива;
- вставлять подзаголовки, разбивая таблицу на группы;
- вставлять ссылки — единичные, или как содержимое таблицы;
Что генератор на данный момент не умеет:
- работать с разными типами данных — все ячейки заполняются как строки. По этой причине невозможно использование формул excel в шаблоне, которые оперировали бы введенными данными;
- вставлять изображения;
- менять форматирование — оно полностью определяется в шаблоне.
В таком виде он проработал вот уже скоро год. Проблемы за этот период возникали в основном только с клиентской частью — со стороны магазина. Хотя, это не значит, что скрипт «генератора» идеален. На сегодняшний день он обкатан только в одной реальной задаче.
Вскоре возникла мысль, что этот продукт может быть полезен и другим людям. Так был зарегистрирован новый домен, на него установлена копия генератора документов и создан небольшой сайт, на котором можно «пощупать» работу скрипта, залить свой шаблон и эксплуатировать до тех пор, пока выдержит железо.
Сегодня я получила разрешение от заказчика выложить проект на Гитхабе и опубликовать эту статью, чем спешу воспользоваться.
Проверить работу скрипта можно на демо-примере.
Исходный код доступен в репозитории.
Комментарии (20)
BoxaShu
01.04.2015 15:51+1Так же писал в свое время подобный генератор отчетов в xlsx и так же оформление и прочее были зашиты в шаблон, но потом узнал про xslt и все встало на свои места. Теперь, код отдельно, оформление отдельно и в любой момент можно поправить шаблон. Чего и Вам желаю.
gaelpa
01.04.2015 19:39+1Зависит от задачи. В моем случае, например, трудно заставить клиентов осваивать xslt или платить за постоянную корректировку шаблонов по их хотелкам.
great_boba
01.04.2015 18:02+2Давно, когда надо было написать подобную вещь…
Я формировал документ в формате xml с тегами и заголовками (как их сохранял Excel), а потом при скачивании кидал в header соответствующий content-type (application/vnd.ms-excel) и все.
Это позволяло не мучиться с внутренней структурой xlsx файла
yurash
02.04.2015 09:17нисколько не критикую, просто интересуюсь — а Google Spreadsheet/Document не рассматривались в качестве альтернативы? Хоть многие excel фичи там отсутствуют, но благодаря google apps script есть очень широкие возможности автоматизации. Генерацию по шаблону там можно сделать за считанные часы (с нуля, достаточно знать javascript). Если конечно, речь не идёт о сотнях документов в день (т.к. есть квоты и ограничения)
floppox Автор
03.04.2015 13:18Был реальный заказчик. Ему нужна была реализация именно в том виде, как есть. Гуглдокс как раз в первую очередь был предложен, но у фирмы уже был налаженный документооборот, и требовалось вписаться в существующую систему.
floppox Автор
12.04.2015 11:30Среди разных вариантов ответа, рискну остановиться на следующем: чтоб получить инвайт на хабр. Можно так?
На самом деле, конечно, мысли об этом 13 месяцев назад не было, были другие, которые уже сложно вспомнить так, чтоб не соврать.
Alexufo
14.04.2015 20:30как реализована подстановка переменных в документе? как обрамлять переменные?
floppox Автор
15.04.2015 11:48Как обрамлять переменные.
На счет реализации, я не уверенна, что поняла ваш вопрос на 100% правильно. Каждый вид переменных обрабатывается разным способом. Одиночные — прямой заменой всех вхождений на значение, табличные — создаются новые ячейки, по количеству входящих элементов, и уже в них производится замена на значения из массива.Alexufo
19.04.2015 00:07переменные имею ввиду типа %firm_name% вставленные в шаблон docx, которые потом заменяются на значения. Причем вставленны именно в ворде не в xml. Как у вас это работает?
floppox Автор
19.04.2015 20:28Простой заменой
Alexufo
20.04.2015 22:41но в xml переменные могут не храниться в строчку. Если вы ошибетесь в написани переменной и исправите ошибку, она будет разбита тегом history и обычной заменой в xml уже не прокатит.
floppox Автор
21.04.2015 07:00Эта проблема решается с помощью регулярных выражений.
github.com/floppox/f-document/blob/master/generator/docx_processor.class.php
Функция search_filds()Alexufo
21.04.2015 08:45В моём случае я решал это покороче, сначала грохал все теги между требуемыми ключевыми символами регуляркой, а после — обычной заменой.
floppox Автор
21.04.2015 10:57Перестановка мест слагаемых. Хотя, возможно, имеет смысл для оптимизации, но это не очевидно — нужны экперименты. Ну а мой выбор обоснован тем, что такой подход показался более контролируемым, чем «грохать все регуляркой».
PQR
Полезная наработка!
Сможете отделить библиотеку генерации документа от сайта (index.php) и завернуть её в composer пакет?
floppox Автор
На гитхабе лежит как раз отдельно только то, что связано с генерацией. Пока ничего, кроме исправления возможных багов, делать не планирую.