Skip to main content

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:

MethodEffect
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.