ChartBuilder
The ChartBuilder class creates charts from database queries and integrates with the same filtering system used by tables and lists. It extends GetDataBuilder, but it does not paginate or limit results, so charts can aggregate the full dataset.
System Overview
ChartBuilder simplifies chart rendering by providing:
- Query-driven charts: Use the fluent
GetDataBuilderAPI to filter and aggregate - Structured datasets: Define axes and datasets with
structure() - Chart.js integration: Render bar, line, pie, table, and other chart types
- AJAX updates:
getResponse()returns chart payloads for live updates - No pagination: The full query result is used to build the chart
Basic Usage
Quick Start
use Builders\ChartBuilder;
$chart = ChartBuilder::create($this->model, 'orders_chart')
->select(['month', 'SUM(total) AS total'])
->groupBy('month')
->orderBy('month', 'ASC')
->structure([
'month' => ['label' => 'Month', 'axis' => 'x'],
'total' => ['label' => 'Total', 'type' => 'bar'],
])
->type('bar')
->render();
echo $chart;
Structure Mapping
structure() maps fields from the SQL result to chart axes and datasets. You must define exactly one X axis; all other fields become datasets.
$chart = ChartBuilder::create($this->model, 'sales_chart')
->select(['month', 'SUM(total) AS total', 'COUNT(id) AS orders'])
->groupBy('month')
->orderBy('month', 'ASC')
->structure([
'month' => ['label' => 'Month', 'axis' => 'x'],
'total' => [
'label' => 'Total',
'type' => 'bar',
'backgroundColor' => '#9BD0F5',
'borderColor' => '#36A2EB',
'borderWidth' => 1,
],
'orders' => ['label' => 'Orders', 'type' => 'line'],
]);
Chart Options
Use options() or option() to pass Chart.js options or plugin-specific settings (like height or legend positioning).
$chart = ChartBuilder::create($this->model, 'orders_chart')
->option('height', 320)
->option('legend_position', 'bottom')
->option('show_datalabels', 'percent');
Using Filters with SearchBuilder
ChartBuilder uses the same filter system as tables. Filter names must match between SearchBuilder and ChartBuilder.
use Builders\ChartBuilder;
use Builders\SearchBuilder;
use App\Response;
$chart_id = 'orders_chart';
$chart = ChartBuilder::create($this->model, $chart_id)
->setRequestAction('home')
->select(['month', 'SUM(total) AS total'])
->filter('status_filter', function($query, $value) {
$query->where('status = ?', [$value]);
})
->groupBy('month')
->orderBy('month', 'ASC')
->structure([
'month' => ['label' => 'Month', 'axis' => 'x'],
'total' => ['label' => 'Total', 'type' => 'bar'],
]);
$search = SearchBuilder::create($chart_id)
->select('status_filter')
->label('Status')
->options([
'' => 'All',
'paid' => 'Paid',
'pending' => 'Pending',
])
->layout('inline');
$response = array_merge($this->getCommonData(), $chart->getResponse());
$response['search_html'] = '';
$response['bottom_content'] = $search->render();
Response::render(MILK_DIR . '/Theme/SharedViews/list_page.php', $response);
Chart Tables
You can render a table from the same chart data. Set type to table and disable pagination with itemsPerPage = 0.
$data = $chart->getChartData();
echo Get::themePlugin('chart', [
'id' => 'orders_table',
'type' => 'table',
'data' => $data,
'options' => [
'preset' => 'hoverable',
'itemsPerPage' => 0,
],
]);
AJAX Response
ChartBuilder::getResponse() returns an array with html and chart keys so the frontend can update the chart without a full page reload.
$response = $chart->getResponse();
// $response['html'] contains the chart wrapper
// $response['chart'] contains { id, type, data, options }
Multiple Charts on One Page
If you render multiple charts in the same view, set a custom canvas id so Chart.js instances do not collide.
$chart = ChartBuilder::create($this->model, 'orders_chart')
->setCanvasId('orders_chart_canvas')
->render();