Ui5Chart
Introduction
The Ui5Chart is a UI5 artifact that renders a single data visualization — a sparkline, trend line, bar comparison, donut, etc. — inside a dashboard group. A chart is computed server-side from bound slot values and the request context, and emits a sap.f.Card envelope wrapping a com.laravelui5.core.Chart payload.
Like Ui5Tile, a chart is not routable: it has no URL, no manifest, and no standalone endpoint. It exists only as part of a Ui5Dashboard, and its whole envelope (header + canvas + layout) is computed by its provider at the moment the dashboard is emitted. The rendering engine itself (ECharts) is host-loaded, not bundled by Core — see ECharts loading.
Conceptual Overview
Two seats, two jobs
The chart class (
Ui5ChartInterface) is pure metadata: identity, the slots it depends on, and a handle to its provider. It is deliberately not a template factory — a chart's option tree is data-driven, so only the executor, which has the data, can build it.The chart provider (
ChartProviderInterface) is the executor. It computes the full chart envelope from the bound slot values and the request context:phppublic function getChart(array $boundParams, Ui5ContextInterface $context): Chart;
Two-DTO composition: Chart wraps ChartCanvas
The wire output is a two-layer envelope — an outer sap.f.Card (with header, optional toolbar, optional layoutData) carrying an inner com.laravelui5.core.Chart (with engine + option). At authoring time that maps to two DTOs:
Chart— the outer envelope. ExtendsAbstractCard. Takes a requiredcanvas: ChartCanvasslot plus optional Header / layoutData / sizing.ChartCanvas— the inner engine-rendered DTO. Carriesengine+option. It is never used on its own: aChartis the only thing that carries one (see There is no second path).
The provider composes both layers explicitly: it builds the Chart and hands it a ChartCanvas. The two-layer shape is visible at every authoring site, mirroring how a Tile provider composes GenericTile → TileContent[] → payload. On the wire, Chart places the canvas inside a sap.m.VBox as the card's content.
Header auto-population
If the provider returns a Chart with header: null, the ChartEmitter auto-builds a Header from the artifact's getTitle() (→ title) and getDescription() (→ subtitle). Providers that want a custom header (status text, icon, timestamp, toolbar) pass an explicit Header through the Chart constructor.
Lifecycle
- The chart is declared in a module and referenced from a dashboard group.
- When the dashboard is emitted, the framework resolves the chart's required slots and calls
getChartProvider()->getChart($boundParams, $context). - The provider returns a
Chart(wrapping aChartCanvas); the framework auto-populates the header from artifact metadata if absent, then places the envelope verbatim into the dashboard tree.
The provider reads the actor from $context, never from a slot — slots carry wire-shaped values, identity comes from the context.
How to Generate
php artisan ui5:chart Finance/RevenueTrend \
--title="Revenue Trend" \
--description="Quarterly revenue over the trailing 12 months"Options
| Option | Default | Description |
|---|---|---|
name (argument) | (required) | Format: {AppName}/{ChartName} |
--title | The chart name | Chart title |
--description | Chart generated via ui5:chart | Chart description |
--seed[=<name>] | (bare) | Scaffold the provider from a named seed (see Seeding) |
The PHP and JS namespaces are derived from the target app's module — there are no namespace-prefix options.
Output
Given php artisan ui5:chart Finance/RevenueTrend:
ui5/
└── Finance/
└── src/
└── Charts/
├── RevenueTrendChart.php
└── Provider/
└── RevenueTrendChartProvider.phpOn success the command prints the exact registration directives — the module method (getCharts()) and the owning Dashboard Group's getChildNamespaces(), each with the literal line to add.
Seeding
By default the ChartProvider is a bare shell — it returns a chart with an empty ECharts option. To scaffold a populated starting point:
php artisan ui5:chart Finance/RevenueTrend --seed=revenue-trend
php artisan ui5:chart Finance/RevenueTrend --seed # pick from the catalog--seed swaps exactly one file — the ChartProvider. An unknown seed name fails with the list of available seeds. The chart seed catalog ships revenue-trend, calls-per-week, pipeline-funnel, quota-attainment and revenue-mix; it is open content — a new seed is a stub plus a seeds.json row, no command change.
Artifact Roles
RevenueTrendChart.php
Implements Ui5ChartInterface. Identity is declared as class constants (the getters are inherited from AbstractUi5Chart); the only behaviour is the handle to its provider:
class RevenueTrendChart extends AbstractUi5Chart
{
public const string NAMESPACE = 'com.acme.finance.charts.revenue-trend';
public const string VERSION = '1.0.0';
public const string TITLE = 'Revenue Trend';
public const string DESCRIPTION = 'Quarterly revenue over the trailing 12 months';
public function getChartProvider(): string
{
return RevenueTrendChartProvider::class;
}
}getChartProvider() declares the provider by class-string; the framework resolves it through the container, so the provider's constructor dependencies are autowired. Never write return app(...) or return new ... here — a declaration method declares, it does not build.
If the chart depends on slot values, declare them with getRequiredSlots(); the resolved values arrive in $boundParams. (See Slots.)
RevenueTrendChartProvider.php
Composes the full envelope. Follow the injection recipe: services go in the constructor (autowired, since the framework resolves the declared class-string through the container); the per-request inputs — $boundParams and $context — arrive as the typed getChart() arguments.
class RevenueTrendChartProvider implements ChartProviderInterface
{
public function __construct(private readonly RevenueRepository $revenue) {}
public function getChart(array $boundParams, Ui5ContextInterface $context): Chart
{
$series = $this->revenue->quarterly();
return new Chart(
canvas: new ChartCanvas(
engine: 'echarts',
option: [
'xAxis' => ['type' => 'category', 'data' => $series->labels()],
'yAxis' => ['type' => 'value'],
'series' => [['type' => 'line', 'data' => $series->values()]],
],
),
layoutData: new GridContainerItemLayoutData(columns: 6, rows: 4),
height: '100%',
);
}
}The option is engine-native JSON; Core does not translate, normalise, or schema-validate it. See the ECharts option reference.
Module Integration
Register charts in their module's getCharts(), passing the module instance:
public function getCharts(): array
{
return [
new Charts\RevenueTrendChart($this),
];
}A chart is rendered by referencing its ::NAMESPACE from a dashboard group's getChildNamespaces() — see Ui5Dashboard. There is no route to configure: charts are composed, not addressed.
There is no second path
A ChartCanvas is only ever carried by a Chart. There is no other container that takes one, and the dashboard's wire vocabulary is closed: the walker in @laravelui5/core accepts sixteen class names, and anything else from the server fails envelope validation with UnknownControlError before it renders.
Two consequences worth knowing before you try:
Chart already is the card. It extends AbstractCard, and the emitter turns it into the two-level wire shape sap.f.Card → com.laravelui5.core.Chart. Wrapping it in another card is not a smaller path, it is a second card.
sap.ui.integration.widgets.Card takes no content at all. Its constructor takes a manifest — an integration card's body comes from its card manifest, rendered on the server, not from a DTO tree assembled in PHP. The two classes share a short name and nothing else.
If a chart needs surrounding chrome — a caption, a toolbar, a second figure — that is what the Chart's own header and the header's toolbar are for, and both are part of the vocabulary.
The working reference
php artisan ui5:assemble Showcase scaffolds five chart artifacts across three dashboard groups — a sparkline, a trend, a funnel, a gauge and a mix. Each one is a provider returning a Chart that wraps a ChartCanvas, exactly as above:
public function getChart(array $boundParams, Ui5ContextInterface $context): Chart
{
return new Chart(
canvas: new ChartCanvas(
engine: 'echarts',
option: [
'xAxis' => ['type' => 'category', 'data' => ['Q1', 'Q2', 'Q3', 'Q4']],
'yAxis' => ['type' => 'value'],
'series' => [['type' => 'bar', 'data' => [120, 200, 150, 80]]],
],
),
height: '100%',
layoutData: new GridContainerItemLayoutData(columns: 2, rows: 2),
);
}That is the generated revenue-trend seed, unedited. See Self-contained apps for the whole assembly, and --seed on ui5:chart for the other four.
ECharts loading
Core ships zero third-party JS. ECharts is loaded by the host via the bootstrap-extension hook (@includeIf('ui5.head')) — typically pinned to a CDN URL or a host-served asset:
{{-- resources/views/ui5/head.blade.php --}}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.min.js"></script>The control reads window.echarts when it renders (onAfterRendering). If it isn't on the page, the chart stays empty and an error is logged; the rest of the dashboard keeps working. See the bootstrap-mechanism spec for the broader extension model.
Best Practices
- Keep the chart class metadata-only; build the visual in the provider.
- Inject services via the provider's constructor; take per-request values from
$boundParams/$context. - Let
ChartEmitterpopulate the header from artifact metadata by default; override with an explicitHeaderonly when you need status text, an icon, or a header toolbar. - The
optionis engine-native — author it directly against the ECharts reference. Don't wrap it in helpers prematurely. - For press-intent on the whole card, set
parameterson theChart; for per-data-point intent, stuff intent onto the ECharts data points and read it in the consumer'spresshandler — see Ui5Dashboard for the action-event shape.
Related Links
- ECharts option reference
<lux:Chart>control — the client side- Ui5Dashboard — composing charts into a dashboard
- Ui5Card — embedding
ChartCanvasinside a card's content tree - Slots — how bound values reach a chart's provider
- Settings — how an artifact declares configuration its provider reads