Queries and segments
get() takes a parameter array, which is fine for one report and awkward for a
dashboard. query() builds the same call fluently, and runs through the same cache and
resilience path:
use MatomoAnalytics\Facades\MatomoReports;
$pages = MatomoReports::query('Actions.getPageUrls')
->period('month')->date('2026-01')
->segment('deviceType==smartphone')
->sortBy('nb_visits')->limit(10)
->flat()
->get();
Nothing happens until get(). The builder is a description of a request, so you can
pass a partly-built query around and finish it elsewhere. toArray() gives you the
parameter array it would send, which is what you want when a report comes back
unexpected — but it is the request, not the response, so it says nothing about what
Matomo did with it.
The filters
They map one-to-one onto Matomo's own report parameters:
| Method | Effect |
|---|---|
period($period) · date($date) | The date range — day, week, month, year, range |
segment($definition) | A raw definition, a named segment, or a Segment builder |
limit($n) · offset($n) | Paginate the rows |
sortBy($column, $order) | Sort; $order defaults to desc |
search($pattern, $column) | Filter rows by a pattern |
truncate($rows) | Collapse everything past $rows into an "Others" row |
flat() | Flatten a hierarchical report into one level |
expanded() | Return the full subtable hierarchy |
showColumns([...]) · hideColumns([...]) | Restrict the columns returned |
params([...]) | Anything else Matomo accepts |
flat() is the one worth knowing about: page-URL reports are hierarchical by path
segment, so without it Actions.getPageUrls gives you top-level folders rather than
pages. truncate() is how you keep a "top 10 plus Others" widget honest — the Others
row carries the remainder rather than dropping it.
params() is the escape hatch for a report parameter the builder does not model, the
same way CustomParameters is on the
write side.
Segments
A segment restricts a report to a subset of visits. There are three ways to supply one, and they exist for three different situations.
A raw definition
MatomoReports::query('VisitsSummary.get')->segment('visitCount>1;actions>=3')->get();
Matomo's own syntax: ; is AND, , is OR. Use this when you copied a segment out of
the Matomo UI.
The builder
use MatomoAnalytics\Reporting\Segment;
$segment = Segment::where('deviceType', '==', 'smartphone')->andWhere('visitCount', '>', 1);
MatomoReports::query('VisitsSummary.get')->segment($segment)->get();
where() starts a segment, andWhere() and orWhere() extend it. Use this when the
segment is composed from application state — a filter a user picked, a tenant
identifier — because it encodes the values rather than concatenating strings.
Operators are validated against Matomo's set: equality and inequality (==, !=),
the comparisons (<, <=, >, >=), contains and does-not-contain (=@, !@), and
starts-with and ends-with (=^, =$). An unsupported operator throws
InvalidArgumentException rather than being sent as a segment Matomo will silently
misread.
That is the right default for an operator you wrote, and the wrong one for an operator a
visitor picked: an unvalidated value from a request turns into a 500. Validate the operator
against the list above before it reaches the builder — Rule::in([...]) in a form request
is enough — or catch the exception where you build the segment.
A named segment
Register the segments you use repeatedly in config and reference them by key:
// config/matomo-analytics.php
'reporting' => [
'segments' => [
'mobile' => 'deviceType==smartphone',
'engaged' => 'visitCount>1;actions>=3',
],
],
MatomoReports::query('VisitsSummary.get')->segment('engaged')->get();
segment() resolves a string against the registry first and falls back to treating it
as a raw definition, so both forms work through the same method. This is the right home
for the segments your dashboards depend on: the definition lives in one place, and a
call site reads as the question it is asking rather than as Matomo syntax.
A segment Matomo has never seen may need to be processed before it returns data, depending on your Matomo's archiving configuration. If a brand-new segment returns empty where the UI shows rows, that is a Matomo-side archiving question rather than a package one.