# KoolReport > KoolReport is an open-source PHP reporting framework (koolreport/core on Packagist; PHP 7.0 or newer, tested up to 8.5) with a commercial Pro bundle (koolreport/pro) of extra widgets and features. A report is a PHP class that pipes data from data sources through processes into data stores, plus a view file that renders widgets (tables, charts, pivot tables, inputs) from those data stores. Current version: 6.8.0 (core and Pro). This is the complete reference for writing KoolReport code. Generated 2026-11-03 from the KoolReport sources; class and method names in it are checked against the code. ## Install Core is on Packagist: ```bash composer require koolreport/core ``` The Pro bundle and every Pro package (pivot, excel, inputs, drilldown, datagrid, chartjs, ...) come from KoolReport's private Composer repository, which needs the customer's credentials: ```json { "repositories": [ {"type": "composer", "url": "https://repo.koolreport.com"} ], "require": { "koolreport/pro": "^6.7" } } ``` Put the credentials in `auth.json` next to `composer.json` (keep it out of version control): `{"http-basic": {"repo.koolreport.com": {"username": "...", "password": "..."}}}`. Without them Composer cannot fetch any Pro package; there is no Packagist fallback. Load KoolReport with Composer's autoloader: `require_once __DIR__ . "/vendor/autoload.php";`. ## Report anatomy A report is three files: 1. `EmployeeReport.php` — a class extending `\koolreport\KoolReport`. `settings()` returns the data source configuration; `setup()` builds the data pipeline into named data stores. 2. `EmployeeReport.view.php` — the view, found by naming convention (class name + `.view.php`, same directory). Inside it, `$this` is the report: use `$this->dataStore("name")` and `$this->params`. 3. `index.php` — creates the report, runs it, renders it. ```php array( "hr" => array( "connectionString" => "mysql:host=localhost;dbname=hr", "username" => "user", "password" => "secret", "charset" => "utf8", ), ), ); } protected function setup() { $this->src("hr") ->query("SELECT department, salary FROM staff WHERE hired >= :since") ->params(array(":since" => $this->params["since"])) ->pipe(new \koolreport\processes\Group(array("by" => "department", "avg" => "salary"))) ->pipe($this->dataStore("salaries")); } } ``` ```php $this->dataStore("salaries"), "columns" => array( "department" => array("label" => "Department"), "salary" => array("label" => "Average salary", "type" => "number", "prefix" => "$", "decimals" => 0), ), )); BarChart::create(array( "dataSource" => $this->dataStore("salaries"), "columns" => array("department", "salary"), )); ``` ```php "2020-01-01")); // becomes $this->params $report->run()->render(); // render() echoes HTML; wrap it in your own page ``` - `run()` executes `setup()` and fills the data stores; `render()` includes the view. `render("OtherView")` uses `OtherView.view.php`; `render(true)` returns the HTML instead of echoing it. - Widgets load their own JS/CSS; KoolReport serves them from the package folders, so `vendor/` must be reachable under the web server's document root (or set the `assets` setting to copy them to a public folder). ## Data sources Each entry in `"dataSources"` is a name => config array. `class` picks the data source; without `class` it is `\koolreport\datasources\PdoDataSource`. The keys differ per class — do not mix them: | class | required keys | notes | |---|---|---| | `PdoDataSource` (default) | `connectionString` (PDO DSN, e.g. `mysql:host=localhost;dbname=shop`), `username`, `password` | `charset` optional. Works for MySQL, PostgreSQL, SQL Server, SQLite, Oracle via PDO. | | `MySQLDataSource` | `host`, `username`, `password`, `dbname` | mysqli. `port` (3306), `charset`. A `connectionString` is ignored here, which gives "No database selected". | | `PostgreSQLDataSource`, `SQLSRVDataSource`, `OracleDataSource` | native drivers | | | `CSVDataSource` | `filePath` | `fieldSeparator` (default `,`), `charset`, `firstRowData` (default false = first row is the header). | | `ArrayDataSource` | `data` | `dataFormat`: `"associate"` (default; list of assoc rows) or `"table"` (first row = column names). | | `ExcelDataSource` (koolreport/excel) | `filePath` | `\koolreport\excel\ExcelDataSource`; `firstRowData`, `sheetName`, `sheetIndex`. | Class names are written as strings: `"class" => "\koolreport\datasources\CSVDataSource"`. Querying: `$this->src("name")->query($sql)` for SQL sources; bind values with `->params(array(":id" => $value))` — never concatenate user input into SQL. Non-SQL sources (CSV, array, Excel) are piped directly: `$this->src("csv")->pipe(...)`. ```php array( "shop" => array( "class" => "\koolreport\datasources\MySQLDataSource", "host" => "localhost", "username" => "user", "password" => "secret", "dbname" => "shop", "charset" => "utf8", ), "targets" => array( "class" => "\koolreport\datasources\CSVDataSource", "filePath" => __DIR__ . "/targets.csv", "fieldSeparator" => ";", ), "manual" => array( "class" => "\koolreport\datasources\ArrayDataSource", "dataFormat" => "table", "data" => array( array("team", "goal"), array("Alpha", 1200), array("Beta", 950), ), ), ), ); ``` ## Processes (the data pipeline) Processes transform rows between a data source and a data store: `$this->src(...)->query(...)->pipe(new Process(...))->pipe(...)->pipe($this->dataStore("x"))`. They live in `koolreport\processes` (core) unless noted. A pipeline can branch: keep a node in a variable and pipe it into several processes or data stores. The processes that exist are listed in the next section. There is no `Cast`, `Cut`, `Rename`, `Where`, `Aggregate` or `Pivot` in `koolreport\processes` — use the ones listed. Most used, with their real parameter shapes: ```php src("shop")->query("SELECT category, sku, price, cost, qty, sold_at FROM sales") // computed columns: a function of the row (see the note below on string expressions) ->pipe(new CalculatedColumn(array( "revenue" => function ($row) { return $row["price"] * $row["qty"]; }, "profit" => function ($row) { return ($row["price"] - $row["cost"]) * $row["qty"]; }, ))) // keep rows matching ALL conditions: array(column, operator, value[, value2]) // operators: = != > >= < <= contain notContain startWith endWith between notBetween in notIn like ->pipe(new Filter(array( array("qty", ">", 0), array("category", "in", array("Bikes", "Parts")), ))) // bucket a date column: date, week, month, quarter, year, hourofday, dayofweek, ... ->pipe(new TimeBucket(array("sold_at" => "month"))) // group: "by" columns, then sum/count/avg/min/max => column list (string or array) ->pipe(new Group(array("by" => "category,sold_at", "sum" => "revenue,profit", "count" => "sku"))) ->pipe(new Sort(array("revenue" => "desc"))) ->pipe(new Limit(array(10))) // array(limit, offset) ->pipe(new ColumnRename(array("sold_at" => "month"))) ->pipe(new OnlyColumn(array("category", "month", "revenue", "profit"))) ->pipe(new ColumnMeta(array("revenue" => array("type" => "number", "prefix" => "$")))) ->pipe(new Custom(function ($row) { $row["category"] = strtoupper($row["category"]); return $row; })) ->pipe($this->dataStore("top_categories")); ``` `CalculatedColumn` also accepts a string expression such as `"{price}*{qty}"`, but string expressions are fragile: in versions before core 6.8.1 they fail on text values (`"{first}.' '.{last}"` breaks on the first name). Always use a function. Three processes take an unusual parameter shape; write them exactly like this: ```php "name, city")); // case => columns (also lower, first-cap, all-cap) new \koolreport\processes\DifferenceColumn(array("change" => "sales")); // new column => source column; no options new \koolreport\processes\NumberRange(array("creditLimit" => array( // label => array(from, to), null = open end "low" => array(null, 50000), "medium" => array(50000, 100000), "high" => array(100000, null), ))); ``` `Group` overwrites the aggregated columns in place: `"sum" => "revenue"` keeps the name `revenue`. `Sort` takes column => "asc"|"desc" (or a compare function). Joining two pipelines: `new \koolreport\processes\Join($left, $right, array("customerId" => "id"))` (also `LeftJoin`, ...). Pivot and Cube processes are in their own packages (see "Pivot tables" and "Cube"). ## Process list The complete list, generated from the package sources. Anything not listed does not exist. - `koolreport\cleandata`: Describe, DoFill, DropNull, FillNull - `koolreport\core`: ProcessGroup, ProcessGroupEnd - `koolreport\cube\processes`: Cube, SuperCube - `koolreport\pivot\processes`: Pivot, Pivot2D, PivotExtract, PivotSQL - `koolreport\processes`: AccumulativeColumn, AggregatedColumn, AppendRow, CalculatedColumn, ColumnMeta, ColumnRename, ColumnsSort, CopyColumn, Count, Custom, DateTimeFormat, DifferenceColumn, Filter, Forward, FullJoin, Group, InnerJoin, Join, JsonColumn, JsonSpread, LeftJoin, Limit, Map, NumberBucket, NumberRange, OnlyColumn, OuterJoin, RemoveColumn, RightJoin, RowNumColumn, Shuffle, Sort, StringCase, StringTrim, TimeBucket, Transpose, Transpose2, TypeAssure, ValueMap - `koolreport\statistics`: Statistics ## Widgets: common settings Every widget is rendered in the view with `ClassName::create(array(...))`. Settings shared by most widgets: - `"dataSource" => $this->dataStore("name")` — the data to show. Pass the data store object: it works in every version. Core 6.8.0 and later also accept the data store's name as a string (`"dataSource" => "name"`); 6.7.1 and earlier fail on that with `count(): Argument #1 must be of type Countable|array`. An array of rows or a pipeline node also works. - `"columns"` — which columns to show and how. A list of names, or name => settings: `label`, `type` (`"number"`, `"string"`, `"datetime"`, `"date"`), `prefix`, `suffix`, `decimals`, `thousandSeparator`, `decimalPoint`, `formatValue` (always a function: `function ($value, $row) { return ...; }`; a string such as `"@value*2"` is fragile and, before core 6.8.1, fails on text columns), `cssStyle`. For charts the first column is the category axis and each further column is a series. - `"title"` for charts; `"width"`, `"height"` for charts. Pixels (`400` or `"400px"`) work everywhere; CSS lengths such as `"100%"` work for most libraries, but not for koolreport/d3 before 1.7.2 (Pro 6.8.0), where they render nothing. - `"options"` — passed through to the underlying JS library (Google Charts, Chart.js, ApexCharts, ECharts, DataTables). Use that library's option names. - `"clientEvents"` — JS event handlers as strings: `"itemSelect" => "function(params){ ... }"`. - `"name"` — a JS variable name for the widget instance (needed for drill-down `next()` calls). Number columns need `"type" => "number"` when the values arrive as strings (CSV, some drivers). ## Widget catalog Every widget class in the Pro bundle, generated from the package sources. Use the full namespace; a class name without the right namespace is the most common error. - `koolreport\amazing`: ChartCard, DualChartCard, GaugeCard, IndicatorCard, ProgressCard, SimpleCard - `koolreport\apexcharts`: AreaChart, BarChart, BoxplotChart, BrushCharts, BubbleChart, CandlestickChart, Chart, ColumnChart, ComboChart, DonutChart, FunnelChart, HeatmapChart, LineChart, PieChart, PolarAreaChart, RadarChart, RadialBarChart, RangeAreaChart, RangeBarChart, ScatterChart, SynchCharts, TimeLineChart, TreemapChart - `koolreport\barcode`: BarCode, QRCode - `koolreport\chartjs`: AreaChart, BarChart, BubbleChart, Chart, ColumnChart, ComboChart, DonutChart, LineChart, PieChart, PolarChart, RadarChart, ScatterChart, Timeline - `koolreport\charttable`: ChartTable - `koolreport\d3`: AreaChart, BarChart, C3Chart, ColumnChart, DonutChart, FunnelChart, GaugeChart, LineChart, PieChart, ScatterChart, SplineChart, Waterfall - `koolreport\datagrid`: DataTables - `koolreport\drilldown`: CustomDrillDown, DrillDown, DrillThrough, LegacyDrillDown, MultiView - `koolreport\echarts`: AreaChart, Bar3DChart, BarChart, BoxplotChart, CandlestickChart, Chart, ChordChart, ColumnChart, ComboChart, CustomChart, DonutChart, FunnelChart, GaugeChart, GraphChart, HeatmapChart, Line3DChart, LineChart, Lines3DChart, Map3DChart, MapChart, ParallelChart, PictorialBarChart, PieChart, RadarChart, SankeyChart, ScatterChart, SunburstChart, ThemeRiverChart, TreeChart, TreemapChart - `koolreport\excel`: AreaChart, AreaChart3D, BarChart, BarChart3D, BubbleChart, CandleChart, Chart, DonutChart, DoughnutChart, Hyperlink, Image, LineChart, LineChart3D, PieChart, PieChart3D, PivotMatrix, PivotTable, RadarChart, ScatterChart, StockChart, SurfaceChart, SurfaceChart3D, Table, Text, Widget - `koolreport\inputs`: BSelect, CheckBoxList, DateRangePicker, DateTimePicker, DateTimePicker2, InputControl, MultiSelect, RadioList, RangeSlider, Select, Select2, TextArea, TextBox - `koolreport\morris_chart`: Area, Bar, Donut, Line, Morris - `koolreport\pivot\widgets`: PivotMatrix, PivotTable - `koolreport\sparklines`: Bar, Box, Bullet, Chart, Line, Pie, Tristate - `koolreport\visualquery`: VisualQuery - `koolreport\widgets\google`: AreaChart, BarChart, BubbleChart, CandlestickChart, Chart, ColumnChart, ComboChart, DonutChart, Gauge, GeoChart, Histogram, LineChart, Map, OrgChart, PieChart, Sankey, ScatterChart, SteppedAreaChart, Table, Timeline, TreeMap, WordTree - `koolreport\widgets\koolphp`: Card, Table ## Table (core) `\koolreport\widgets\koolphp\Table` renders an HTML table server-side. ```php $this->dataStore("orders"), "columns" => array( "customer" => array("label" => "Customer", "footerText" => "Total"), "amount" => array("type" => "number", "prefix" => "$", "footer" => "sum"), ), "showFooter" => true, // or "top"; the footer row only renders when this is set "paging" => array("pageSize" => 25), "cssClass" => array("table" => "table table-striped"), )); ``` Column `footer` is an aggregate (`sum`, `avg`, `min`, `max`, `count`) and `footerText` may use `@value`. Before core 6.8.0 the footer is not shown unless `"showFooter"` is set; from 6.8.0 it is shown when a column defines one, and `"showFooter" => false` hides it. Setting `"showFooter"` explicitly works in every version. ## Chart libraries and their namespaces Chart classes share names across libraries; the namespace picks the library. These are the only correct namespaces (the full class lists are in the widget catalog below): | library | package | namespace | examples | |---|---|---|---| | Google Charts | core | `koolreport\widgets\google` | `ColumnChart`, `BarChart`, `LineChart`, `PieChart`, `DonutChart`, `AreaChart`, `ComboChart`, `GeoChart` | | Chart.js | koolreport/chartjs | `koolreport\chartjs` | `ColumnChart`, `BarChart`, `LineChart`, `PieChart`, `DonutChart` | | ApexCharts | koolreport/apexcharts | `koolreport\apexcharts` | `ColumnChart`, `LineChart`, `AreaChart`, `PieChart`, `DonutChart` | | ECharts | koolreport/echarts | `koolreport\echarts` | `ColumnChart`, `BarChart`, `LineChart`, `PieChart`, `DonutChart` | | D3 / C3 | koolreport/d3 | `koolreport\d3` | `ColumnChart`, `BarChart`, `LineChart`, `PieChart`, `DonutChart` | | Morris | koolreport/morris_chart | `koolreport\morris_chart` | `Line`, `Area`, `Bar`, `Donut` | | Sparklines | koolreport/sparklines | `koolreport\sparklines` | `Line`, `Bar`, `Pie` | There is no `koolreport\googlecharts` or `koolreport\google` namespace. Two libraries differ from the rest: - Morris (`koolreport\morris_chart`) is a jQuery plugin and the widget does not load jQuery. The page must load jQuery in `` first, or the chart fails with "jQuery is not defined". - Sparklines (`koolreport\sparklines`) draw one series, chosen with `"column" => "amount"` (singular). Sparklines 1.5.0 and earlier ignore `"columns"` and chart the first column of the data. ```php "Signups per week", "dataSource" => $this->dataStore("signups"), "columns" => array( "week" => array("label" => "Week"), "signups" => array("label" => "Signups", "type" => "number"), "trials" => array("label" => "Trials", "type" => "number"), ), )); ``` ## DataTables grid (koolreport/datagrid) `\koolreport\datagrid\DataTables` wraps the DataTables JS grid; everything in `"options"` is a DataTables option. ```php $this->dataStore("products"), "columns" => array("name", "vendor", "price" => array("type" => "number", "prefix" => "$")), "options" => array("searching" => true, "paging" => true, "pageLength" => 25, "order" => array(array(2, "desc"))), )); ``` For large tables add `"serverSide" => true` with a `"method" => "post"`; the report must then be run per request. ## Pivot tables (koolreport/pivot) Two parts: the `Pivot` process (namespace `koolreport\pivot\processes`) in `setup()`, and the `PivotTable` or `PivotMatrix` widget (namespace `koolreport\pivot\widgets`) in the view. ```php src("shop") ->query("SELECT region, YEAR(sold_at) AS year, category, revenue FROM sales") ->pipe(new \koolreport\pivot\processes\Pivot(array( "dimensions" => array("column" => "year", "row" => "region, category"), "aggregates" => array("sum" => "revenue", "count" => "revenue"), ))) ->pipe($this->dataStore("sales_pivot")); ``` ```php $this->dataStore("sales_pivot"), "measures" => array("revenue - sum"), "rowCollapseLevels" => array(0), )); ``` Measures are named `" - "`. ## Cube / cross-tab (koolreport/cube) `\koolreport\cube\processes\Cube` turns rows into a two-dimensional cross-tab: one output row per `row` value, one column per distinct `column` value (plus `{{all}}`), each cell aggregated with one of `sum`, `count`, `avg`, `min`, `max`, `count percent`, `sum percent`, `count distinct`. ```php src("support") ->query("SELECT agent, priority, ticket_id FROM tickets") ->pipe(new \koolreport\cube\processes\Cube(array( "row" => "agent", "column" => "priority", "count" => "ticket_id", ))) ->pipe($this->dataStore("tickets_by_agent")); ``` Show the result with any table widget, e.g. `\koolreport\widgets\koolphp\Table`. ## Drill-down (koolreport/drilldown) `\koolreport\drilldown\DrillDown` shows one level at a time. Each level has a `title` and a `content` function that receives the accumulated `$params`; a chart's client event calls `.next({...})` to go one level down. Data is queried inside the view with `$this->src(...)`, because each level depends on the clicked value. ```php "regionDrill", "title" => "Revenue", "levels" => array( array( "title" => "All regions", "content" => function ($params, $scope) { ColumnChart::create(array( "dataSource" => $this->src("shop")->query("SELECT region, SUM(revenue) AS revenue FROM sales GROUP BY region"), "columns" => array("region", "revenue" => array("type" => "number")), "clientEvents" => array( "itemSelect" => "function(params){ regionDrill.next({region: params.selectedRow[0]}); }", ), )); }, ), array( "title" => function ($params, $scope) { return $params["region"]; }, "content" => function ($params, $scope) { ColumnChart::create(array( "dataSource" => $this->src("shop") ->query("SELECT city, SUM(revenue) AS revenue FROM sales WHERE region = :region GROUP BY city") ->params(array(":region" => $params["region"])), "columns" => array("city", "revenue" => array("type" => "number")), )); }, ), ), )); ``` ## Inputs and filter forms (koolreport/inputs) Inputs bind to report params. In the report class use the traits `\koolreport\inputs\Bindable` and `\koolreport\inputs\POSTBinding` (or `GETBinding`), give defaults in `defaultParamValues()` and map param => input name in `bindParamsToInputs()`. Read the values with `$this->params` in `setup()`. ```php array("2024-01-01 00:00:00", "2024-03-31 23:59:59"), "priorities" => array("high"), ); } protected function bindParamsToInputs() { return array("period" => "periodInput", "priorities" => "priorityInput"); } protected function settings() { return array("dataSources" => array("support" => array( "connectionString" => "mysql:host=localhost;dbname=support", "username" => "u", "password" => "p", ))); } protected function setup() { $this->src("support") ->query("SELECT DISTINCT priority FROM tickets") ->pipe($this->dataStore("priority_options")); $this->src("support") ->query("SELECT ticket_id, opened_at, priority FROM tickets WHERE opened_at BETWEEN :start AND :end AND priority IN (:priorities)") ->params(array( ":start" => $this->params["period"][0], ":end" => $this->params["period"][1], ":priorities" => $this->params["priorities"], )) ->pipe($this->dataStore("tickets")); } } ``` ```php that posts back to the same page use \koolreport\inputs\DateRangePicker; use \koolreport\inputs\Select2; ?>
"periodInput")); ?> "priorityInput", "multiple" => true, "dataStore" => $this->dataStore("priority_options"), "dataBind" => "priority", )); ?>
``` Other inputs in `koolreport\inputs`: `TextBox`, `Select`, `MultiSelect`, `BSelect`, `CheckBoxList`, `RadioList`, `DateTimePicker`, `RangeSlider`. An array param bound with `IN (:name)` is expanded by KoolReport. ## Export to Excel (koolreport/excel) Add the trait `\koolreport\excel\ExcelExportable` to the report class, then in a separate endpoint (e.g. `export.php`) run the report and export. There is no `exportToXLSX()` on this trait (that is `\koolreport\excel\BigSpreadsheetExportable`, for very large data). CSV and JSON come from a separate trait, `\koolreport\excel\CSVExportable`: `$report->run()->exportToCSV("tickets")->toBrowser("tickets.csv")` (the argument is a data store name), and `exportToJSON(...)` the same way. A report can use both traits. Simplest Excel export — data stores as sheets: ```php run() ->exportToExcel(array("dataStores" => array("tickets" => array()))) ->toBrowser("tickets.xlsx"); // or ->saveAs("/path/tickets.xlsx") ``` With a template — `exportToExcel("TicketReportExcel")` renders `TicketReportExcel.view.php`, whose structure is fixed: each top-level `
` is a worksheet and each div inside it is one block (text, `\koolreport\excel\Table`, `\koolreport\excel\PivotTable`, charts). Any other markup (for example `` elements) has no content: excel 12.0.0 and earlier (Pro 6.7.1) export an empty workbook without an error, and excel 12.0.1 (Pro 6.8.0) throws an exception that explains the required structure. ```php
Tickets by priority
$this->dataStore("tickets"))); ?>
``` ## Query builder (koolreport/querybuilder) Build SQL with `\koolreport\querybuilder\DB::table(...)` and pass it to `query()` wrapped in the dialect: `\koolreport\querybuilder\MySQL::type($query)` (also `PostgreSQL`, `SQLServer`, `Oracle`, `SQLite`). Aggregates take only the column; name the result with `->alias()` right after: ```php src("shop") ->query(MySQL::type( DB::table("sales") ->join("stores", "stores.id", "=", "sales.store_id") ->select("stores.city") ->sum("sales.revenue")->alias("revenue") ->count("sales.id")->alias("orders") ->where("sales.sold_at", ">=", "2024-01-01") ->groupBy("stores.city") ->orderBy("revenue", "desc") ->limit(10) )) ->pipe($this->dataStore("top_cities")); ``` There is no `sum($column, $alias)` form and no `QueryBuilderEnabled` trait. ## KPI card (core) ```php "Open tickets", "value" => $this->dataStore("open_count")->get(0, "total"), // a number, or a data store / query "baseValue" => 120, // optional, shows the change "format" => array("value" => array("decimals" => 0)), )); ``` `DataStore::get($row, $column)` reads one cell; `->sum("col")`, `->avg("col")`, `->count()`, `->min()`, `->max()` aggregate a whole store. ## Common mistakes Each line is a mistake AI assistants make with KoolReport, measured in an eval. - Wrong: `use koolreport\googlecharts\PieChart; / koolreport\google\BarChart` → Right: `koolreport\widgets\google\PieChart`. Google Charts widgets live in koolreport\widgets\google. - Wrong: `"dataSource" => "sales"` → Right: `"dataSource" => $this->dataStore("sales")`. Core 6.7.1 and earlier fail on a string; the data store object works in every version. - Wrong: `new \koolreport\processes\Cast(...) / Cut(...)` → Right: `ColumnMeta for types, OnlyColumn/RemoveColumn for columns`. Those classes do not exist; see the process list. - Wrong: `$report->exportToXLSX("View") with ExcelExportable` → Right: `$report->run()->exportToExcel("View")->toBrowser("file.xlsx")`. exportToXLSX belongs to BigSpreadsheetExportable. - Wrong: `Excel template using elements` → Right: `
...
`. Only the two-level div structure is read; anything else gives an empty workbook (excel 12.0.0) or an exception (12.0.1+). - Wrong: `"class" => MySQLDataSource with "connectionString"` → Right: `MySQLDataSource takes host/dbname/username/password; or omit class and keep connectionString (PDO)`. Otherwise: No database selected. - Wrong: `->sum("qty", "total")` → Right: `->sum("qty")->alias("total")`. Query builder aggregates take one argument. - Wrong: `"footer" => "sum" without showFooter` → Right: `add "showFooter" => true`. Before core 6.8.0 no footer renders otherwise; explicit showFooter works in every version. - Wrong: `koolreport\d3 chart with "width" => "100%"` → Right: `a pixel width, e.g. "width" => 600, or upgrade to d3 1.7.2 (Pro 6.8.0)`. d3 1.7.1 and earlier pass it to C3 as a number and draw nothing. - Wrong: `CalculatedColumn with "{first}.' '.{last}" or a formatValue string like "@value km"` → Right: `function ($row) { return $row["first"] . " " . $row["last"]; }`. String expressions break on text values before core 6.8.1; functions work in every version. - Wrong: `$report->render() before run()` → Right: `$report->run()->render()`. render() shows empty data stores if setup() never ran. ## Widget options Generated from the widget sources and the documentation (tools/registry). Type and default come from the code; `—` means none is set. ### koolreport\apexcharts\BrushCharts | option | type | default | description | |---|---|---|---| | `brushMap` | — | '[]' | | | `columns` | array/mixed | — | Specify the list of columns will be used to draw chart | ### koolreport\apexcharts\Chart | option | type | default | description | |---|---|---|---| | `autoCategoryColumn` | bool|string|array | '$this->autoCategoryColumn' | Auto-generate categories. `true` = numeric (1,2,3…); `"alphabet"` = A,B,C…; `"numeric"` = 1,2,3…; array = custom label list | | `clientEvents` | array | 'array()' | KoolReport widget-level event handlers — not ApexCharts' own events. A map of event name => JavaScript function string; the chart fires `init`, `drawing` a... | | `colorScheme` | array | — | set colors for single or multiple data series | | `colors` | array | — | the same as colorScheme | | `columns` | array/mixed | — | Specify the list of columns will be used to draw chart | | `cssStyles` | array | '[]' | set css styles for chart's parent wrapper div element | | `fillOpacity` | number/array | — | set series' color filling opacity | | `fillType` | string/array | — | set series' color filling type | | `height` | string/number | 'auto' | set chart's height in pixel or relative percentage | | `jsFiles` | array | '[]' | Extra JavaScript files to load for the chart. A list of known helper names (`moment`, `dayjs`, `dayjsQuarter`) or custom file names resolved under the apexch... | | `keepNullValue` | boolean | '$this->defaultKeepNullValue' | allow null values in series or not | | `markersSize` | number/array | — | set series' markers' sizze | | `maxWidth` | string/numer | 'null' | set chart's maximum width in pixel or relative percentage | | `onBeforeInit` | mixed | — | JavaScript function string invoked before the chart's client-side creation, as `(function() {...})(widgetName);`. | | `optionArrayDelimiter` | string | '$this->optionArrayDelimiter' | set delimiter/separator for options string properties | | `options` | array | 'array()' | Specify extra options for charts, see extra options section for more details. | | `series` | array | — | The chart's series data in ApexCharts format. When supplied (top-level or via `options` => `['series' => ...]`), `dataSource` becomes optional and the column... | | `showForSingleSeries` | bool | '$showLegend' | Whether to show the legend when the chart has a single series. When absent, inherits `showLegend` (itself `false` when `sparkline` is enabled). | | `showLabel` | boolean | 'false' | show labels or not | | `showLegend` | boolean | '$sparklineEnabled ? false : true' | show legends or not | | `showMarker` | boolean | 'true' | show markers or not | | `showStroke` | boolean | 'true' | show stroke or not | | `showTooltip` | boolean | 'true' | show tooltips or not | | `showXaxis` | boolean | — | show Xaxis or not | | `showYaxis` | boolean | — | show Yaxis or not | | `sparkline` | boolean | — | set chart to sparkline mode for small size charts | | `stackType` | string | — | Specify if the series of chart will be stacked. | | `stacked` | boolean | — | Specify if the series of chart will be stacked. | | `strokeCurve` | string/array | 'smooth' | set series' stroke curve type | | `strokeWidth` | number/array | — | set series' stroke width | | `subtitle` | string | — | Subtitle of the chart | | `subtitleAlignment` | string | 'center' | Subtitle alignment | | `title` | string | '' | Title of the chart | | `titleAlignment` | string | 'center' | Title alignment | | `type` | string | 'bar' | The ApexCharts chart type. Only honoured on the base `Chart` class; concrete chart classes (BarChart, LineChart, ...) preset their own type and ignore this p... | | `uniqueCategories` | boolean | '$this->defaultUniqueCategories' | set unique or duplicated categories | | `width` | string/numer | '100%' | set chart's width in pixel or relative percentage | | `widthHeightAutoRatio` | number | — | set chart's width to height ratio in case at least one of them is not fixed in pixel | | `xTitle` | string | — | Xaxis title of the chart | | `yTitle` | string | — | Title text for the Y axis, mapped to ApexCharts `yaxis.title.text`. | ### koolreport\apexcharts\SynchCharts | option | type | default | description | |---|---|---|---| | `columns` | array/mixed | — | Specify the list of columns will be used to draw chart | ### koolreport\chartjs\Chart | option | type | default | description | |---|---|---|---| | `additionalPlugins` | — | 'array()' | | | `backgroundColor` | — | 'self::AUTO_COLOR' | | | `backgroundOpacity` | number | 0.5 | Specify opacity of bar background | | `borderColor` | — | 'self::AUTO_COLOR' | | | `chartjsClient` | — | '$chartjsClientDefault' | | | `clientEvents` | — | 'array()' | | | `colorScheme` | — | — | | | `columns` | array/mixed | — | Specify the list of columns will be used to draw chart | | `data` | array | — | An array of data. To be used in case `dataSource` is null | | `height` | — | — | | | `inlinePlugins` | array | '[]' | Specify direct inline plugins. | | `options` | array | 'array()' | Specify extra options for charts, see extra options section for more details. | | `plugins` | array | 'array()' | Specify multiple plugins for charts. | | `title` | string | — | Title of the charts | | `type` | — | — | | | `width` | — | — | | ### koolreport\widgets\koolphp\Card | option | type | default | description | |---|---|---|---| | `baseValue` | mixed | — | The reference value for the trend indicator (the percentage change from it is shown). Accepts a scalar, or a DataStore/DataSource/Process object (first cell ... | | `cssClass` | array | 'array()' | CSS classes for card elements. A map of element => class string; keys: `card`, `value`, `indicator`, `title`, `negative`, `positive`, plus `upIcon`/`downIcon... | | `cssStyle` | array | 'array()' | Inline CSS styles for card elements. A map of element => CSS declaration string; keys: `card`, `value`, `indicator`, `title`, `negative`, `positive`. The `ne... | | `format` | array | 'array()' | Number/string formatting for the value and the indicator. A map with `value` and `indicator` keys, each a format config (e.g. `["prefix" => "$"]`); the indic... | | `href` | string | — | Make the card clickable. A URL, or a JavaScript function string (starting with `function`) invoked on click. | | `indicator` | string | 'percent' | How to compute the trend indicator. Accepts `"percent"` (percentage change vs `baseValue`), `"different"` (absolute difference), or a callable `function($val... | | `indicatorTitle` | string | 'Compared to previous {baseValue}' | Tooltip title for the indicator. `{baseValue}` and `{value}` placeholders are replaced with the formatted values. | | `title` | string | — | The card's title. Accepts a plain string, or a callable `function($value)` returning the title (called with the card's value). | | `value` | mixed | — | The primary value displayed on the card. Accepts a scalar, or a DataStore/DataSource/Process object (its first row's first column is used). | ### koolreport\widgets\google\Chart | option | type | default | description | |---|---|---|---| | `clientEvents` | array | 'array()' | Client-side event handlers. A map of event name => JavaScript function string. Handlers are invoked for the wrapper's own events: `itemSelect`, `rowSelect`, ... | | `colorScheme` | array | — | List of colors applied to chart, for example: `"colorScheme"=>array("#000","#444","#888")` | | `columns` | array | 'null' | List of columns and its settings | | `data` | array | — | The chart's data as a PHP array of associative rows. Column types are guessed from the first row. Prefer `dataSource`/`dataStore` for datastore-backed charts. | | `height` | string | '400px' | Height of chart in `px` or `%` | | `language` | string | '$this->language' | Locale passed to Google's `google.charts.load()` for chart UI text (tooltips, legends). | | `mapsApiKey` | string | '' | Google Maps API key, required by map-based chart packages (GeoChart, Map) when Google must load the Maps API. | | `options` | array | 'array()' | This is the options for specific charts. It may vary from charts to charts so for more information, please look at example for specific chart below and its o... | | `pointerOnHover` | bool | — | Show a pointer (hand) cursor over chart elements on hover. | | `stability` | string | '$this->stability' | Version selector for Google's Charts loader. Commonly `current` or `latest`; an explicit version string is also accepted and forwarded verbatim. | | `title` | string | — | Title of chart | | `type` | string | — | The Google visualization chart class name (e.g. `"ColumnChart"`, `"PieChart"`, `"GeoChart"`). Only honoured on the base `Chart` class; concrete classes set i... | | `width` | string | '100%' | Width of chart in `px` or `%` | ### koolreport\widgets\google\Table | option | type | default | description | |---|---|---|---| | `clientEvents` | array | — | Settings for table client events | | `columns` | array | 'array()' | List of columns to display and their settings | | `cssClass` | array | 'array()' | Set css class for table, row and cell | | `data` | array | — | The table's rows as a PHP array of associative rows. Column types are guessed from the first row. Prefer `dataSource`/`dataStore` for datastore-backed tables. | | `emptyValue` | mixed | 0 | Value rendered in a cell whose data key is missing or `null`. Note: it does NOT replace empty strings or `0` values, only absent/null cells. | | `excludedColumns` | array | 'array()' | Column keys to hide from the table. A list of column names that are dropped from the rendered columns. | | `groupCellsInColumns` | array | '$rowspan' | An alias of `removeDuplicate` | | `grouping` | array | — | Group rows by column value. A map of column key => config, or column key => column-name string (shorthand). Config keys: `top`, `bottom`, `calculate`, `css`,... | | `headers` | array | 'array()' | Extra header rows above the column labels. A list of rows; each row is a map of header text => attribute array (`["colSpan" => "2", "align" => "center"]`), w... | | `paging` | array | — | Settings for table paging | | `removeDuplicate` | array | 'array()' | List of column names or orders that you want to remove duplicate | | `responsive` | bool | 'false' | Wrap the table in a horizontally-scrollable container. | | `rowspan` | array | '$removeDuplicate' | An alias of `removeDuplicate` | | `showFooter` | mixed | — | Show a footer row with aggregated column values. `"top"` renders the footer above the body; any other truthy value (e.g. `true`, `"bottom"`) renders it below. | | `showHeader` | bool | 'true' | Render the `` (the `headers` group rows and the column-label row). Set `false` to suppress the header entirely. | | `sorting` | array | 'array()' | Sort the table by column. An associative map of column name => `"asc"`/`"desc"`, or column name => comparator callable. | ### koolreport\d3\C3Chart | option | type | default | description | |---|---|---|---| | `clientEvents` | — | 'array()' | | | `colorScheme` | — | — | | | `height` | — | — | | | `options` | — | 'array()' | | | `width` | — | — | | ### koolreport\datagrid\DataTables | option | type | default | description | |---|---|---|---| | `DataTablesCss` | string | — | Override the DataTables CSS file to load. A file name resolved under the DataTables clients folder (defaults to the theme's bundled `datatables` stylesheet). | | `DataTablesJs` | string | — | Override the DataTables JavaScript library file to load. A file name resolved under the DataTables clients folder (defaults to the theme's bundled `datatable... | | `ajaxIdParam` | string | 'id' | Name of the request parameter carrying the row id in server-side edit/delete requests. | | `ajaxUrl` | string | 'null' | Override the AJAX endpoint used for server-side data requests, for when it differs from the current report URL. | | `attributes` | array | 'array()' | Optional. You could set custom attributes for the table's table, th, tr, td, tf elemens. | | `clientEvents` | array | '[]' | Map of DataTables event name => JavaScript function string, registered on the DataTable with `dt.on(event, fn)`. Note that when row grouping is active the wi... | | `clientRowGroup` | array | '[]' | Group rows client-side by column. A map of column key => config with keys `direction` (default `asc`), `calculate`, `top`, `bottom`. Adds the RowGroup plugin... | | `clientRowSpan` | array | '[]' | Merge vertically-consecutive identical cells client-side for the listed columns, and sort by them. Accepts a column key => config map (`['direction' => 'asc'... | | `collapseIcon` | string | '' | HTML for the collapse icon shown in client row-group headers. Injected into the `{expandCollapseIcon}` placeholder of a group's `top`/`bottom` templates. | | `columns` | array | 'array()' | Optional. List all the columns you want to display in data table together with its settings | | `complexHeaderLabels` | boolean | 'false' | Optional. Apply complex headers on column labels instead of column keys. | | `complexHeaders` | boolean | '$this->complexHeaderLabels' | Optional. Use DataTables' complex headers with rowspan and colspan. Defaults to the value of `complexHeaderLabels` (itself `false`), so enabling `complexHead... | | `cssClass` | array | 'array()' | Optional. You could set css classes for the table's table, th, tr, td, tf elemens. | | `cssStyle` | array | 'array()' | Inline CSS styles for table elements. A map of element => CSS declaration string or callable. Keys: `table`, `th`, `tr`, `td`, `tf` (server-rendered), plus `... | | `data` | array | — | Optional. You may set data directly here in form of associate array | | `defaultPlugins` | array | '["AutoFill", "ColReorder", "RowGroup", "Select"]' | Optional. Set DataTables' default plugins to load with. | | `deleteFunction` | callable | — | PHP callable invoked server-side to perform a row delete. Called with the row's `deleteParams` (from `$_POST[]['deleteParams']`); its return value is JSON-en... | | `editButtons` | array | '[]' | Custom buttons for the row-edit column. A map of button key => settings merged over the built-in `add`/`edit`/`delete` buttons. | | `emptyValue` | string | '-' | Optional. Empty value is used to replace missing data in rows. | | `expandIcon` | string | '' | HTML for the expand icon shown in client row-group headers. Injected into the `{expandCollapseIcon}` placeholder of a group's `top`/`bottom` templates. | | `fastRender` | bool | 'false' | Render all rows client-side for large tables. The server serializes the numeric-indexed rows and DataTables draws them with `deferRender`; the `` is not serv... | | `groupCellsInColumns` | array | '$rowspan' | Merge vertically-consecutive identical cells for the listed columns (server-side, via the RowsGroup plugin). An alias of `rowspan` — see the `rowspan` row.... | | `headerSeparator` | string | '-' | Optional. If `complexHeaders` == true, use this separator to separate headers' groups. | | `insertFunction` | callable | — | PHP callable invoked server-side to perform a row insert. Called with the row's `insertParams` (from `$_POST[]['insertParams']`); the return value is merged ... | | `method` | string | 'get' | HTTP method for server-side (AJAX) data requests: `get` or `post`, case-insensitive. With server-side processing the request body is read from `$_GET`/`$_POS... | | `name` | string | — | Optional. You can set the name for your data table if you want to refer to the table later on at client-side. If you don't set we will set random name for table | | `onBeforeInit` | mixed | — | JavaScript function string invoked once, before the widget's client-side init, as `(function() {...})()`. | | `options` | array | 'array()' | Optional. This property will hold all the extra settings for data tables | | `overrideSearchInput` | bool | 'true' | Whether the widget rebinds the DataTables search input. Set `false` when a plugin such as mark.js needs the original binding. | | `plugins` | array | '[]' | Optional. Set DataTables' additional plugins to load with. | | `removeDuplicate` | array | '[]' | Merge vertically-consecutive identical cells for the listed columns (server-side, via the RowsGroup plugin). An alias of `rowspan` — see the `rowspan` row.... | | `rowDetail` | array | '[]' | Row-detail settings as one array. Its `data` key is an alias of `rowDetailData`. | | `rowDetailData` | mixed | — | HTML shown in an expandable row detail. Accepts a PHP closure `function($row){...}` (called per row, result rendered into a hidden column) or a JavaScript fu... | | `rowDetailIcon` | bool | 'true' | Show the expand/collapse icon for row detail. Set `false` to expand by clicking the row itself. | | `rowDetailSelector` | string | '' | CSS selector for the element that toggles row detail, used when `rowDetailIcon` is `false`. | | `rowDetailServerRender` | bool | 'false' | Render row-detail content server-side into a hidden column instead of building it on the client. | | `rowGroup` | array | '[]' | Group rows server-side with section headers and aggregates. A map of column key => config with keys `direction` (default `asc`), `calculate`, `top`, `bottom`... | | `rowspan` | array | '$removeDuplicate' | Merge vertically-consecutive identical cells for the listed columns (server-side, via the RowsGroup plugin). Accepts column names (when `columns` is a plain ... | | `scope` | array | 'array()' | Optional. Data table will include the scope parameters in each of its ajax call to server-side operation. | | `searchMode` | string | 'smart' | Optional. One or more of the modes `smart`, `or`, `and`, `exact` combined with the `\|` separator. Matching is case-insensitive, so `"OR"` and `"or"` are equi... | | `searchOnEnter` | boolean | 'false' | Optional. Make DataTables' filter only when users press Enter on the input field. | | `searchQuery` | string | '$originalQuery' | SQL query string used for server-side searching, with a `{datatables_search}` placeholder the widget replaces with the generated search conditions. Defaults ... | | `serverRowGroup` | array | '$this->serverRowGroup' | Group rows server-side with section headers and aggregates. A map of column key => config with keys `direction` (default `asc`), `calculate`, `top`, `bottom`... | | `serverSide` | boolean | 'false' | Optional. Default value is false. Determine whether data table's operations are performed on server-side or client-side (default). | | `serverSideInstantSearch` | bool | 'false' | Enable instant (as-you-type) searching with server-side processing. May also be set via `serverSide` => `['instantSearch' => ...]`. | | `showFooter` | boolean | 'false' | Optional. Show table footer or not, default value is false. | | `template` | string | 'DataTables' | Name of the template view used to render the widget, resolved under the package's template folder (a theme-specific variant is preferred when one exists). | | `unminifiedCss` | bool | 'false' | Load the unminified DataTables CSS by dropping the `.min` suffix from the default stylesheet names. Has no effect on an explicit `DataTablesCss` override. | | `unminifiedJs` | bool | 'false' | Load the unminified DataTables JavaScript by dropping the `.min` suffix from the default and plugin script names. Has no effect on an explicit `DataTablesJs`... | | `updateFunction` | callable | — | PHP callable invoked server-side to perform a row update. Called with the row's `updateParams` (from `$_POST[]['updateParams']`); the return value is merged ... | ### koolreport\drilldown\DrillDown | option | type | default | description | |---|---|---|---| | `btnBack` | mixed | 'true' | By default, the button Back will shown, give it value `false` you will off the Back button. This property can receive array() to customize cssClass and text ... | | `clientEvents` | array | 'array()' | Register client-event, see client-event for more details. | | `css` | mixed | — | Defined css for the most important elements in the widgets, see the __$css__ properties for more details. | | `cssClass` | mixed | — | Defined css class for the most important elements in the widgets, see the __$cssClass__ properties for more details. | | `currentLevel` | — | 'array(0, array())' | | | `global` | — | — | | | `levels` | array | 'array()' | `* Required` List of all levels for drill down report. See the __level properties__ for more detail of settings on each level. | | `name` | string | — | `*Required` Name of the drill down report or even process. | | `partialRender` | — | 'false' | | | `scope` | array/function | 'array()' | Set the group of parameters which will be kept persistent during the ajax post back of drill-down. | | `showLevelTitle` | bool | 'true' | Whether title of each level is shown | | `template` | — | — | | | `title` | string | 'DrillDown' | Title that is showed on top of drill-down report. | ### koolreport\echarts\Chart | option | type | default | description | |---|---|---|---| | `clientEvents` | — | '[]' | | | `colorScheme` | — | — | | | `columns` | — | — | | | `height` | — | '400px' | | | `options` | — | '[]' | | | `series` | — | — | | | `title` | — | — | | | `width` | — | '100%' | | ### koolreport\inputs\DateRangePicker | option | type | default | description | |---|---|---|---| | `caret` | — | 'fa fa-caret-down' | | | `format` | — | — | | | `icon` | — | 'glyphicon glyphicon-calendar fa fa-calendar' | | | `maxDate` | — | — | | | `minDate` | — | — | | | `options` | — | 'array()' | | | `ranges` | — | 'array("Today" => DateRangePicker::today(), "Yesterday" => DateRangePicker::yesterday(), "Last 7 days" => DateRangePicker::last7days(), "Last 30 days" => DateRangePicker::last30days(), "This month" => DateRangePicker::thisMonth(), "Last month" => DateRangePicker::lastMonth())' | | | `template` | — | — | | ### koolreport\inputs\Select2 | option | type | default | description | |---|---|---|---| | `defaultOption` | — | 'null' | | | `multiple` | — | 'false' | | | `options` | — | 'array()' | | | `placeholder` | — | — | | | `singleNoneSelected` | — | 'false' | | ### koolreport\pivot\widgets\PivotTable | option | type | default | description | |---|---|---|---| | `clientEvents` | — | '[]' | | | `columnCollapseLevels` | — | 'array()' | | | `columnTotalAtBeginning` | — | 'false' | | | `cssClass` | — | 'array()' | | | `emptyValue` | — | '-' | | | `hideGrandTotalColumn` | — | 'false' | | | `hideGrandTotalRow` | — | 'false' | | | `hideSubTotalColumns` | — | 'false' | | | `hideSubTotalRows` | — | 'false' | | | `hideSubtotalColumn` | — | 'Util::get($this->params, \'hideSubTotalColumns\', false)' | | | `hideSubtotalColumns` | — | 'Util::get($this->params, \'hideSubtotalColumn\', Util::get($this->params, \'hideSubTotalColumns\', false))' | | | `hideSubtotalRow` | — | 'Util::get($this->params, \'hideSubTotalRows\', false)' | | | `hideSubtotalRows` | — | 'Util::get($this->params, \'hideSubtotalRow\', Util::get($this->params, \'hideSubTotalRows\', false))' | | | `id` | — | 'null' | | | `initVisible` | — | 'false' | | | `name` | — | '$this->name' | | | `paging` | — | 'array()' | | | `rowCollapseLevels` | — | 'array()' | | | `rowTotalAtBeginning` | — | 'false' | | | `selectable` | — | 'false' | | | `showDataHeaders` | — | 'false' | | | `template` | — | 'PivotTable' | | | `totalName` | — | 'Total' | | | `width` | — | 'null' | |