Отчеты и графики — это как хлеб для бизнеса. Без них никуда: директор хочет видеть динамику продаж, менеджер — статистику по клиентам, а вы — чтобы всё это работало быстро и не разваливалось. В Yii2 для этого есть масса инструментов, но выбрать правильный путь не всегда просто. В этой статье я покажу, как строить сложные отчеты и графики, используя современные подходы и библиотеки. Вы узнаете, как не утонуть в коде и сделать так, чтобы отчеты были не только красивыми, но и полезными.
С чего начать: выбираем инструменты для отчетов
Прежде чем писать код, давайте подумаем, что мы вообще строим. Отчеты бывают разные: простые таблицы, сводные данные, диаграммы, дашборды. Для каждого типа — свой инструмент. В Yii2 часто используют:
- GridView — встроенный виджет для таблиц с сортировкой, фильтрацией и пагинацией.
- Chart.js — легкая JavaScript-библиотека для красивых графиков.
- Highcharts — более мощная, но платная для коммерции.
- PhpSpreadsheet — для экспорта в Excel, когда нужно скачать отчет в файл.
Если вам нужно построить сложный отчет с группировками и итогами, GridView может быть маловат. Тогда придется писать собственные запросы с использованием Query Builder или DAO. Но не переживайте — это не так страшно, как звучит.
Как выбрать? Ориентируйтесь на задачи. Если отчет нужен для внутреннего использования — подойдут бесплатные решения. Если это клиентский продукт — возможно, стоит вложиться в Highcharts.
Строим сложный отчет с помощью Query Builder
Допустим, у нас есть интернет-магазин. Нужно построить отчет по продажам за месяц с разбивкой по категориям товаров и дням. Это классика. В Yii2 мы можем собрать данные одним запросом, используя Query Builder.
Создадим модель для отчета. Например, в контроллере:
public function actionSalesReport() {
$from = Yii::$app->request->get('from', date('Y-m-01'));
$to = Yii::$app->request->get('to', date('Y-m-t'));
// Строим запрос
$query = (new Query())
->select([
'DATE(o.created_at) as day',
'c.name as category',
'SUM(oi.quantity * oi.price) as total',
'COUNT(DISTINCT o.id) as orders_count'
])
->from(['o' => 'orders'])
->leftJoin(['oi' => 'order_items'], 'oi.order_id = o.id')
->leftJoin(['p' => 'products'], 'p.id = oi.product_id')
->leftJoin(['c' => 'categories'], 'c.id = p.category_id')
->where(['between', 'DATE(o.created_at)', $from, $to])
->groupBy(['DATE(o.created_at)', 'c.name'])
->orderBy('day');
$rows = $query->all();
// Передаем в вид
return $this->render('sales-report', [
'rows' => $rows,
'from' => $from,
'to' => $to,
]);
}
Здесь мы используем Query из Yii2, который позволяет строить запросы без написания сырого SQL. Это безопасно и удобно. Обратите внимание на группировку и агрегатные функции — они дают нам нужные итоги.
Полученные данные уже можно показать в виде таблицы в представлении. Но для сложности добавим итоги по каждой категории за период. Это можно сделать прямо в PHP:
// Группируем данные по категориям для итогов
$totals = [];
foreach ($rows as $row) {
$cat = $row['category'];
if (!isset($totals[$cat])) {
$totals[$cat] = 0;
}
$totals[$cat] += $row['total'];
}
Теперь у нас есть массив с итогами, и мы можем вывести его внизу таблицы.
Добавляем графики с Chart.js
Таблица — это хорошо, но глаз хочет наглядности. Графики помогают увидеть тренды. Для этого подключим Chart.js. Он прост в использовании и отлично работает с Yii2.
Сначала подключим библиотеку через AssetBundle. Создадим свой ассет:
namespace app\assets;
use yii\web\AssetBundle;
class ChartAsset extends AssetBundle
{
public $sourcePath = '@npm/chart.js/dist';
public $js = [
'chart.umd.js'
];
public $depends = [
'yii\web\YiiAsset',
];
}
Затем зарегистрируем его в представлении и передадим данные из контроллера в JavaScript. Например, для графика продаж по дням:
// В контроллере подготовим данные для графика
$days = [];
$totalsByDay = [];
foreach ($rows as $row) {
$days[] = $row['day'];
$totalsByDay[] = $row['total'];
}
В представлении:
Видите, как просто? Всего несколько строк кода — и у нас интерактивный график. Chart.js сам обрабатывает масштаб, подписи и анимации.
Экспорт отчета в Excel с PhpSpreadsheet
Часто отчеты нужно не только смотреть на экране, но и скачивать. Например, для бухгалтерии. Для экспорта в Excel отлично подходит библиотека PhpSpreadsheet. Установите её через Composer:
composer require phpoffice/phpspreadsheet
Теперь создадим действие для экспорта:
public function actionExportSales() {
// Получаем те же данные, что и для отчета
$rows = ...; // здесь код из actionSalesReport
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
// Заголовки
$sheet->setCellValue('A1', 'День');
$sheet->setCellValue('B1', 'Категория');
$sheet->setCellValue('C1', 'Сумма');
$sheet->setCellValue('D1', 'Кол-во заказов');
// Данные
$rowNum = 2;
foreach ($rows as $row) {
$sheet->setCellValue('A' . $rowNum, $row['day']);
$sheet->setCellValue('B' . $rowNum, $row['category']);
$sheet->setCellValue('C' . $rowNum, $row['total']);
$sheet->setCellValue('D' . $rowNum, $row['orders_count']);
$rowNum++;
}
// Отдаем файл на скачивание
$filename = 'sales_report_' . date('Ymd') . '.xlsx';
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment; filename="' . $filename . '"');
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
$writer->save('php://output');
Yii::$app->end();
}
Важно не забыть вызвать Yii::$app->end() после отправки файла, чтобы Yii не выполнял дальнейший код.
Оптимизация производительности для больших данных
Когда данных много (миллионы строк), простые запросы могут тормозить. Что делать?
- Используйте индексы в базе данных на поля, по которым фильтруете и группируете.
- Грузите данные пачками, если нужно обработать всё, но не помещается в память.
- Для агрегатов используйте кэширование: если данные меняются редко, можно кэшировать результат на 5-10 минут.
- Вместо того чтобы получать все строки, делайте выборку только нужных полей.
Пример кэширования в Yii2:
$cacheKey = 'sales_report_' . $from . '_' . $to;
$rows = Yii::$app->cache->get($cacheKey);
if ($rows === false) {
// Выполняем запрос
$rows = $query->all();
// Кэшируем на 300 секунд
Yii::$app->cache->set($cacheKey, $rows, 300);
}
Это простой способ снизить нагрузку на базу, если отчет запрашивают часто.
Правила и советы по работе с отчетами
Вот несколько правил, которые я выработал за годы практики:
- Всегда фильтруйте по датам. Отчет без периода — это не отчет, а бессмысленный список.
- Используйте агрегатные функции на стороне БД, а не в PHP. Так быстрее и надежнее.
- Не выводите все данные сразу. Используйте пагинацию или ограничение количества записей.
- Проверяйте права доступа. Отчеты могут содержать конфиденциальные данные.
Итоги
Построение сложных отчетов и графиков в Yii2 — задача решаемая. Главное — правильно подобрать инструменты и структурировать код. Используйте Query Builder для выборки данных, Chart.js для визуализации, а PhpSpreadsheet для экспорта. Не забывайте про кэширование и оптимизацию.
В итоге у вас будет быстрый и удобный отчет, который порадует и вас, и вашего директора. И не бойтесь экспериментировать — Yii2 дает много свободы.
Студия WNDER

