FastExcelTemplator

Источник: 3.4.0 · 4de0b13

На этой странице

🇬🇧 English · 🇷🇺 Русский

FastExcelTemplator — часть проекта FastExcelPhp, в который входят

Введение

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.4
    avadim/fast-excel-writer ^6.16.1

    avadim/fast-excel-helper устанавливается транзитивно через эти пакеты.

Как это работает

Понимание устройства объясняет и то, что библиотека делает без усилий, и то, где её границы.

  • Потоковая обработка. Данные листа читаются и записываются по строкам. Весь лист не загружается в память; расход памяти также зависит от стилей, метаданных и количества захваченных образцов строк.
  • Результат собирается из шаблона. Библиотека заменяет данные листов и сохраняет исходные части архива и метаданные, включая изображения, примечания, условное форматирование и проверки данных. При вставке или удалении строк диапазоны метаданных автоматически не перестраиваются; подробности приведены в матрице поддержки XLSX.
  • Запись только вперёд. Нельзя вернуться и изменить уже записанную строку. Захват образцов и сохранение метаданных используют отдельные чтения шаблона, не требуя загрузки листа целиком.
  • Формулы переносятся, но не вычисляются. Библиотека записывает текст формулы и не считает её результат — это делает Excel при открытии файла. Когда захваченный образец строки вставляется в другую строку, его формулы перебазируются автоматически (ссылки A1 сдвигаются на целевую строку), так что =C7*D7 в строке-образце становится =C8*D8, =C9*D9 и так далее.

Использование шаблонов

Пример шаблона

demo1-tpl.png

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

demo1-out.png

Шаг 1 — открыть шаблон и задать замены

php
// Открываем шаблон и указываем выходной файл
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 — перенести верх листа и заголовки таблицы из шаблона в выходной файл

php
// Переносим строки 1–6 из шаблона в выходной файл
$sheet->transferRowsUntil(6);

Из шаблона прочитано 6 строк, в выходном файле тоже 6 строк

demo1-out.png

Шаг 3 — вставить внутренние строки таблицы

php
// Берём строку 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

demo1-out.png

Шаг 4 — теперь переносим оставшиеся строки и сохраняем файл

php
// Метод transferRows() без аргументов переносит оставшиеся строки шаблона в вывод
$sheet->transferRows();

// ...
// Сохраняем новый файл
$excel->save();

demo1-out.png

Примеры кода можно найти в папке /demo

Изменение таблиц

Метод rows() читает строки, изменяет их через колбэк и пишет в выходной файл.

php
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();

В колбэке можно добавить одну или несколько ячеек в конец строки. Стили и значение из исходной ячейки будут скопированы в новую. Если явно не указать исходную ячейку, за образец берётся последняя ячейка строки.

php
$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;
});

Также можно клонировать любую ячейку (со стилями и значением) в другую ячейку

php
$sheet->rows(function ($sourceRowNum, $targetRowNum, $rowData) {
    // Клонировать ячейку 'A' в 'E' и присвоить формулу SUM()
    $rowData->cloneCell('A', 'E')
        ->withValues(['=SUM(A' . $targetRowNum . ':E' . $targetRowNum . ')']);

    return $rowData;
});

Чтобы удалить ячейки, используйте removeCells().

php
$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):

php
// Захватываем две по-разному оформленные строки-образца (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 по коллекции. Отсутствующие строки внутри допустимого диапазона образцов возвращаются как пустые образцы.

php
$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():

php
$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 ограничения и записывает замечания в диагностику. Например, диапазоны проверок данных и условного форматирования сохраняют координаты шаблона при вставке строк.

php
$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-заголовками:

php
$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 :)

FastExcelTemplator 3.4.0 · Поиск по текущей библиотеке и версии.

Загрузка поиска…

Не удалось загрузить поиск. Попробуйте ещё раз.

Ничего не найдено. Попробуйте другое слово или имя метода.