FastExcelTemplator
На этой странице
- Введение
- Какая библиотека мне нужна?
- Установка
- Требования
- Как это работает
- Использование шаблонов
- Изменение таблиц
- Повторяющиеся строки и чередование стилей
- Перенос и пропуск строк
- Работа с несколькими листами
- Сохранение результата
- Проверка переноса метаданных
- Отдача файла в браузер
- Ограничения
- FAQ
- Список функций
- Вам нравится FastExcelTemplator?
🇬🇧 English · 🇷🇺 Русский
FastExcelTemplator — часть проекта FastExcelPhp, в который входят
- FastExcelWriter — создание Excel-таблиц
- FastExcelReader — чтение Excel-таблиц
- FastExcelTemplator — генерация Excel-таблиц из XLSX-шаблонов
- FastExcelLaravel — специальная редакция для Laravel
Введение
FastExcelTemplator генерирует Excel-совместимые таблицы в формате XLSX (Office 2007+) из XLSX-шаблонов — очень быстро и с минимальным расходом памяти. Библиотека спроектирована лёгкой, очень быстрой и экономной по памяти.
Возможности
- Поддерживает только формат XLSX (Office 2007+), в том числе книги с несколькими листами
- Переносит из шаблона в результат стили, изображения и примечания
- Заменяет как значения ячеек целиком, так и подстроки
- Любую строку шаблона можно использовать как образец для вставки и замены строк новыми значениями
- Библиотека умеет читать параметры оформления ячеек — форматы, цвета, границы, шрифты и т. д.
Какая библиотека мне нужна?
«Работа с Excel в PHP» — это на самом деле три разные задачи, и под каждую в семействе FastExcelPhp есть свой инструмент:
| Ваша задача | Инструмент |
|---|---|
| Прочитать данные из существующего файла | FastExcelReader |
| Построить таблицу с нуля из кода | FastExcelWriter |
| Заполнить готовый XLSX-бланк данными | FastExcelTemplator (эта библиотека) |
Берите FastExcelTemplator, когда у вас уже есть свёрстанный XLSX-документ (счёт, акт, договор, отчёт) и нужно лишь подставить в него данные — сохранив логотип, границы, форматы чисел и формулы, которые уже есть в файле. Если вы ловите себя на том, что воспроизводите в коде оформление, которое уже существует в файле, — вам нужен шаблонный подход.
Установка
Установите FastExcelTemplator в проект через composer:
composer require avadim/fast-excel-templator
Требования
-
PHP >= 7.4 с расширениями
zip,json,mbstring,xmlreaderиdom. -
Composer подтягивает соседние пакеты автоматически. Текущая линейка 3.x зависит от:
Пакет Ограничение avadim/fast-excel-reader^4.4avadim/fast-excel-writer^6.16.1avadim/fast-excel-helperустанавливается транзитивно через эти пакеты.
Как это работает
Понимание устройства объясняет и то, что библиотека делает без усилий, и то, где её границы.
- Потоковая обработка. Данные листа читаются и записываются по строкам. Весь лист не загружается в память; расход памяти также зависит от стилей, метаданных и количества захваченных образцов строк.
- Результат собирается из шаблона. Библиотека заменяет данные листов и сохраняет исходные части архива и метаданные, включая изображения, примечания, условное форматирование и проверки данных. При вставке или удалении строк диапазоны метаданных автоматически не перестраиваются; подробности приведены в матрице поддержки XLSX.
- Запись только вперёд. Нельзя вернуться и изменить уже записанную строку. Захват образцов и сохранение метаданных используют отдельные чтения шаблона, не требуя загрузки листа целиком.
- Формулы переносятся, но не вычисляются. Библиотека записывает текст формулы и не считает её результат — это делает Excel при открытии файла. Когда захваченный образец строки вставляется в другую строку, его формулы перебазируются автоматически (ссылки A1 сдвигаются на целевую строку), так что
=C7*D7в строке-образце становится=C8*D8,=C9*D9и так далее.
Использование шаблонов
Пример шаблона

Из этого шаблона можно получить такой файл

Шаг 1 — открыть шаблон и задать замены
// Открываем шаблон и указываем выходной файл
use avadim\FastExcelTemplator\Excel;
$excel = Excel::template($tpl, $out);
// Берём первый лист
$sheet = $excel->sheet();
$fillData = [
'{{COMPANY}}' => 'Comp Stock Shop',
'{{ADDRESS}}' => '123 ABC Street',
'{{CITY}}' => 'Peace City, TN',
];
// Замена значений ячеек целиком по всему листу.
// Значение '{{COMPANY}}' будет заменено,
// а 'Company Name {{COMPANY}}' — нет
$sheet->fill($fillData);
// Замена любых встречающихся подстрок.
// В значениях '{{DATE}}' или 'Date: {{DATE}}' подстрока '{{DATE}}' будет заменена
$replaceData = ['{{BULK_QTY}}' => 12, '{{DATE}}' => date('m/d/Y')];
$sheet->replace($replaceData);
fill() vs replace() — самые частые грабли:
fill()заменяет значение, только если вся ячейка равна ключу. Ячейка{{COMPANY}}заменится; ячейкаCompany Name {{COMPANY}}— нет.replace()заменяет ключ как подстроку, в любом месте текста ячейки.
Обе карты применяются к каждой ячейке, которую библиотека пишет в вывод, — и к переносимым, и к вставляемым строкам.
Шаг 2 — перенести верх листа и заголовки таблицы из шаблона в выходной файл
// Переносим строки 1–6 из шаблона в выходной файл
$sheet->transferRowsUntil(6);
Из шаблона прочитано 6 строк, в выходном файле тоже 6 строк

Шаг 3 — вставить внутренние строки таблицы
// Берём строку 7 как образец и переходим к следующей строке шаблона
$rowTemplate = $sheet->getRowTemplate(7);
// Заполняем образец строки и вставляем его в вывод
foreach ($allData as $record) {
$rowData = [
// В колонку A запишется значение из поля 'number'
'A' => $record['number'],
// В колонку B — значение из поля 'description'
'B' => $record['description'],
// И так далее...
'C' => $record['price1'],
'D' => $record['price2'],
];
$sheet->insertRow($rowTemplate, $rowData);
}
Мы заполнили и вставили строки 7, 8 и 9

Шаг 4 — теперь переносим оставшиеся строки и сохраняем файл
// Метод transferRows() без аргументов переносит оставшиеся строки шаблона в вывод
$sheet->transferRows();
// ...
// Сохраняем новый файл
$excel->save();

Примеры кода можно найти в папке /demo
Изменение таблиц
Метод rows() читает строки, изменяет их через колбэк и пишет в выходной файл.
use avadim\FastExcelTemplator\Excel;
$excel = Excel::template($tpl, $out);
$sheet = $excel->sheet();
$sheet->rows(function ($sourceRowNum, $targetRowNum, $rowData) {
// $rowData — экземпляр RowTemplate
// пропустить первую строку
if ($sourceRowNum === 1) {
return null;
}
// прервать обработку, если значение ячейки 'A' больше 5
if ($rowData->getValue('A') > 5) {
return false;
}
// записать значение в ячейку 'B'; если ячейки 'B' нет, она будет создана
$rowData->setValue('B', $rowData->getValue('A') * 2);
// вернуть изменённую строку
return $rowData;
});
$excel->save();
В колбэке можно добавить одну или несколько ячеек в конец строки. Стили и значение из исходной ячейки будут скопированы в новую. Если явно не указать исходную ячейку, за образец берётся последняя ячейка строки.
$sheet->rows(function ($sourceRowNum, $targetRowNum, $rowData) {
// Клонировать последнюю ячейку строки, добавить в конец и присвоить значение 123
$rowData->appendCell()->withValue(123);
return $rowData;
});
$sheet->rows(function ($sourceRowNum, $targetRowNum, $rowData) {
// Клонировать ячейку 'B' и добавить в конец
$rowData->appendCell('B');
// Клонировать последнюю ячейку три раза
$rowData->appendCell(null, 3)->withValues([111, 222, 333]);
return $rowData;
});
Также можно клонировать любую ячейку (со стилями и значением) в другую ячейку
$sheet->rows(function ($sourceRowNum, $targetRowNum, $rowData) {
// Клонировать ячейку 'A' в 'E' и присвоить формулу SUM()
$rowData->cloneCell('A', 'E')
->withValues(['=SUM(A' . $targetRowNum . ':E' . $targetRowNum . ')']);
return $rowData;
});
Чтобы удалить ячейки, используйте removeCells().
$sheet->rows(function ($sourceRowNum, $targetRowNum, $rowData) {
// Удалить ячейки B и D текущей строки со сдвигом оставшихся ячеек влево
$rowData->removeCells(['B', 'D']);
return $rowData;
});
removeCell() и removeCells() изменяют текущую строку, а не структуру колонок всей книги. Значение, стиль и горизонтальное объединение перемещаются вместе; объединение сужается при удалении ячейки внутри него. Сдвиг, затрагивающий объединение из нескольких строк, вызывает исключение. Подробные правила формульных ссылок приведены в матрице поддержки.
Чтобы очистить содержимое и формулу ячейки, сохранив стиль, вызовите $rowData->setValue('B', null). Ноль, false и пустая строка остаются отдельными допустимыми значениями. Строка, начинающаяся с =, записывается как формула.
Повторяющиеся строки и чередование стилей
getRowTemplate($n) берёт одну строку как переиспользуемый образец. Чтобы повторять блок из нескольких строк — или чередовать стили строк («зебра») — захватите диапазон через getRowTemplates($min, $max):
// Захватываем две по-разному оформленные строки-образца (7 и 8)
$rowTemplates = $sheet->getRowTemplates(7, 8);
foreach ($allData as $record) {
// insertRow() циклически перебирает образцы: 7, 8, 7, 8, ...
$sheet->insertRow($rowTemplates, ['A' => $record['number']]);
}
Оба метода возвращают RowTemplateCollection; insertRow() на каждый вызов берёт из неё следующий образец и по достижении конца начинает сначала.
Каждая вставка работает с копией образца. Пропущенные в $cellData колонки сохраняют значения и формулы образца; значения предыдущей вставки не наследуются. Сам образец не изменяется. Цикл вставки независим от foreach по коллекции. Отсутствующие строки внутри допустимого диапазона образцов возвращаются как пустые образцы.
$template = $sheet->getRowTemplate(7);
$sheet->insertRow($template, ['A' => 1, 'B' => 'Первое значение']);
$sheet->insertRow($template, ['A' => 2]); // B берётся из строки 7 шаблона
По умолчанию захват образца продвигает основной курсор до конца запрошенного диапазона. Передайте true последним аргументом — getRowTemplate(7, true) или getRowTemplates(7, 8, true) — чтобы сохранить положение основного курсора. Диапазон должен быть положительным и упорядоченным; при наличии dimension он проверяется по границам листа. Пустая коллекция не может использоваться для вставки.
nextTemplate() возвращает следующий образец в том же цикле, которым пользуется insertRow(). Он независим от foreach, next() и rewind(). Добавление или удаление образца начинает цикл вставок заново. Публичный next() сохраняет прежнее циклическое поведение.
Перенос и пропуск строк
| Метод | Обрабатываемые позиции исходного листа | Результат |
|---|---|---|
transferRows($count) |
Следующие $count позиций |
Строки копируются в вывод |
skipRows($count) |
Следующие $count позиций |
Чтение продвигается без записи |
transferRowsUntil($row) |
До строки $row включительно |
Строки копируются в вывод |
skipRowsUntil($row) |
До строки $row включительно |
Чтение продвигается без записи |
null означает обработку до конца листа, 0 ничего не делает, отрицательное значение вызывает исключение. Пустые позиции разреженного листа входят в количество. Методы Until не поглощают следующую существующую строку за границей диапазона. Повторное обращение после конца листа не добавляет строки.
Callback переноса получает исходный номер строки, целевой номер и RowTemplate. Верните строку для записи, null для её пропуска или false для остановки. При false текущая исходная строка уже прочитана, но не записана; следующий вызов продолжает со следующей строки.
Работа с несколькими листами
Шаблон может содержать несколько листов. Обратитесь к листу по имени через sheet($name) или переберите все листы через sheets():
$excel = Excel::template($tpl, $out);
foreach ($excel->sheets() as $sheet) {
$sheet->fill(['{{TITLE}}' => 'Report']);
$sheet->transferRows();
}
$excel->save();
Листы можно обрабатывать в любом порядке. При сохранении каждый результат сопоставляется с исходной XML-частью листа, даже если порядок листов книги отличается от номеров sheetN.xml.
Сохранение результата
Сохраняйте результат явно через save(). Книга сначала собирается во временном файле в каталоге назначения, затем готовый файл заменяет результат. Ошибка генерации или размещения не удаляет прежний результат. Каталог назначения должен существовать и разрешать создание файлов.
save($path, false) запрещает перезапись, включая файл, появившийся во время генерации. Этот режим использует создание жёсткой ссылки и требует её поддержки файловой системой. Сохранение по пути исходного шаблона запрещено: выберите отдельный выходной файл. Уничтожение объекта не сохраняет книгу автоматически.
Проверка переноса метаданных
По умолчанию сохранение допускает перечисленные в матрице поддержки XLSX ограничения и записывает замечания в диагностику. Например, диапазоны проверок данных и условного форматирования сохраняют координаты шаблона при вставке строк.
$excel = Excel::template($tpl, $out, ['strict_metadata' => true]);
foreach ($excel->sheets() as $sheet) {
$sheet->fill(['{{TITLE}}' => 'Отчёт'])->transferRows();
}
$excel->save(); // исключение при обнаруженном неподдерживаемом преобразовании
foreach ($excel->metadataDiagnostics() as $item) {
// Поля: sheet — путь XML-части, code — код, message — пояснение
error_log($item['sheet'] . ': ' . $item['code'] . ' — ' . $item['message']);
}
В строгом режиме диагностика доступна и после перехвата исключения; сохранение останавливается на первом обнаруженном ограничении. Прежний выходной файл остаётся целым. Коды и пример обработки исключения приведены в описании диагностики.
Отдача файла в браузер
Кроме save(), готовый файл можно отправить клиенту напрямую с нужными HTTP-заголовками:
$excel->download('invoice.xlsx'); // ставит заголовки скачивания и отдаёт файл
$excel->output('invoice.xlsx'); // output() — псевдоним download()
download() отдаёт один файл на ответ, поэтому чтобы передать сразу много документов, сохраните их и упакуйте в архив самостоятельно.
Ограничения
Сохранение метаданных, правила изменения ссылок и ограничения структурных операций описаны в матрице поддержки XLSX. Чтобы отклонять неподдерживаемые преобразования, передайте ['strict_metadata' => true] третьим аргументом Excel::template(). metadataDiagnostics() возвращает замечания последней попытки сохранения. setValue('A', null) очищает содержимое и формулу ячейки с сохранением оформления.
FastExcelTemplator сделан под одну задачу — заполнение XLSX-шаблонов — и сознательно делает не всё:
- Только XLSX (Office 2007+). Старый бинарный
.xlsнельзя использовать как шаблон; сначала пересохраните его в.xlsx. - Запись только вперёд. Нельзя изменить уже записанную строку. Захват образцов и сохранение метаданных используют отдельные чтения.
- Формулы не вычисляются. Библиотека записывает текст формулы (и перебазирует его); результат считает Excel при открытии файла.
- Плейсхолдеры — только подстановка значений: логики внутри ячейки нет (никаких
if/циклов). Таблица неизвестной длины делается через образцы строк, а не через плейсхолдеры. - Это не ридер и не запись с нуля. Чтобы прочитать данные из файла, используйте FastExcelReader; чтобы построить таблицу целиком из кода — FastExcelWriter.
FAQ
Плейсхолдер не заменился.
Проверьте fill() vs replace(). fill() заменяет только ячейку, значение которой в точности равно ключу; если метка сидит внутри другого текста (например, Date: {{DATE}}), используйте replace(), который ищет подстроки.
Формула отображается как текст, или ячейка пуста, пока не открою файл.
Библиотека записывает формулы, но не вычисляет их — результат считает Excel при открытии файла. Убедитесь, что значение — настоящая формула, начинающаяся с =.
Как сохранить логотип / изображения / примечания из шаблона?
При обычной подстановке fill()/replace() и переносе строк сохраняются исходные изображения, примечания и их связи. При структурных изменениях привязки остаются в координатах шаблона; это отражается в диагностике и отклоняется строгим режимом.
Получаю Allowed memory size exhausted на большом файле.
Библиотека работает потоково, поэтому узкое место обычно — ваш источник данных. Тяните строки из базы курсором/генератором, а не загружайте их все в массив (fetchAll()) перед циклом вставки.
Можно ли использовать старый файл .xls как шаблон?
Нет. Поддерживается только XLSX (Office 2007+). Пересохраните .xls в .xlsx (например, в Excel или LibreOffice) и используйте результат как шаблон.
Список функций
В 3.4.0 добавлены независимые вставки из образца, новые правила нулевого количества строк и строгая проверка метаданных. Перед обновлением прочитайте замечания о совместимости. Будущие изменения будут перечислены в разделе Unreleased.
Вам нравится FastExcelTemplator?
Если пакет оказался полезным, вы можете поддержать автора и угостить его чашкой кофе:
- USDT (TRC20) TSsUFvJehQBJCKeYgNNR1cpswY6JZnbZK7
- USDT (ERC20) 0x5244519D65035aF868a010C2f68a086F473FC82b
- ETH 0x5244519D65035aF868a010C2f68a086F473FC82b
Или просто поставьте звезду на GitHub :)