Перейти к содержанию

Php spreadsheet

У объекта $sheet есть интересный метод fromArray(). Перым параметром передаётся набор данных, вторым параметром передаётся стартовая ячейка, от которой начинается внесение данных (по умолчанию A1). Достаточно передать в этот метод двумерный массив данных, метод сам внесёт данные в Xlsx файл:

$data = [
    ['Имя', 'Фамилия'],
    ['Вася', 'Петров'],
    ['Коля', 'Сидоров'],
];
$sheet->fromArray($data);

Удобно отдельно хранить набор колонок, которые должны выводиться в отчёте, а отдельно данные.

Пример структуры массива, описывающего набор колонок:

$columns = [
    'ID'   => ['title' => 'Выводимый заголовок колонки в Excel'],
    'NAME' => ['title' => 'Имя пользователя'],
];
$data = [
    ['ID' => 123, 'NAME' => 'Вася'],
    ['ID' => 456, 'NAME' => 'Петя'],
];
Организовав данные в таком виде, становится очень легко дорабатывать, так как набор колонок, порядок их отображения и отображаемые названия хранятся отдельно от данных. Но как быть с вложенными данными? Можно написать метод получения данных по dot-нотации:
/**
 * Получение значения из вложенного ассоциативного массива по цепочке ключей, разделённых точкой:
 * getDotNotationValue($users, 'USER.NAME') -> $users['USER']['NAME']
 */
getDotNotationValue(array $data, string $key, $default = '')
{
    return '';
}

Перед вызовом шаблона собёрем массив для быстрого формирования xlsx файла

class UsersXlsxReportComponent extends CBitrixComponent
{
    public function executeComponent()
    {
        $data = $this->getData(); // получение необходимых данных
        // дополнение, форматирование, ещё какие-либо манипуляции с данными
        $columns = $this->getColumns();
        $report = $this->getReport($columns, $data);
        $this->arResult = [
            'report' => $report,
        ];
        $this->includeComponentTemplate();
    }

    /**
     * Получение данных
     */
    public function getData()
    {
        return [
            ['ID' => 4321, 'NAME' => 'Вася'],
            ['ID' => 4321, 'NAME' => 'Вася'],
        ];
    }

    /**
     * Набор колонок для вывода в Excel
     */
    private function getColumns(): array
    {
        return [
            'ID' => ['title' => 'User Id'],
            'NAME' => ['title' => 'User name'],
        ];
    }

    /**
     * Формирование отчёта в удобном для быстрого формирования Excel виде
     */
    private function getReport(array $columns, array $data): array
    {
        $report = [];
        $report[] = array_column($columns, 'title');
        foreach ($data as $dataItem) {
            $row = [];
            foreach ($columns as $column) {
                $row[] = $this->$getDotNotationValue($dataItem, $column, '');
            }
            $report[] = $row;
        }
        return $report;
    }

    /**
     * Получение значения в dot-нотификации
     */
    private function getDotNotationValue(array $data, string $key, $default = '')
    {

    }
}

Пример содержимого template.php:

use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;

$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();

$highestColumn = Coordinate::stringFromColumnIndex(count($arResult['headers']));
$pCellRange = sprintf('A1:%s1', $highestColumn);
// оформление заголовочной строки
$sheet->getStyle($pCellRange)->applyFromArray([
    'font'      => ['bold' => true],
    'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER],
]);

// автоширина по содержимому
for ($i = 'A'; $i <= $highestColumn; $i++) {
    $sheet->getColumnDimension($i)->setAutoSize(true);
}

// фиксация заголовочной строки. Дублирование - чтоб корректно отработал setSelectedCell();
$sheet->freezePane('A2', 'A2');

$sheet
    ->fromArray($arResult['headers']);
    ->fromArray($arResult['rows'], null, 'A2')
    ->setSelectedCell('A2') // выбранная ячейка. При открытии Excel на этой ячейке будет фокус
;

global $APPLICATION;
$APPLICATION->RestartBuffer();

header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=utf-8');
header('Content-Disposition: attachment;filename="Название файла.xlsx"');

$xlsx = new Xlsx($spreadsheet);
$xlsx->save('php://output');