Index

StatisticController

MMO\core\controllers · 1 symbol · generated 5 Oct 2026

class

StatisticController

StatisticController.php:19

Compute statistics shaped as mmo-chart's config (counts/sums per dataset, optionally as a percentage, over one or more comparison periods) over an entity collection, exposed both as an API (routeCount, as a file routeExport,

class MMO\core\controllers\StatisticController extends MMO\core\classes\aDataController
Namespace
MMO\core\controllers
Extends
aDataController
See
routeDaysWithout) and as HTML fragments for emails (mailHistogram/mailTendency/mailGraph).

Index

API_PARAMETERS_INFO
COUNT_PARAMETERSThe query parameters of routeCount and routeExport.
query
ExportLines($statistics) staticThe lines of a file holding statistics: one per series, its name first, then one value per period.
getRouteCollection($route)The repository and the collection a statistics route names.
getRouteStatistics($route, $repository, $collection)
getStatistics($collection, $filters, $datasets, $valueField, $percentFilters, $from, $period, $comparisonCount, $comparisonPeriod, $colors, $dateField, $repository)Build a chart dataset: for the current period and each of $comparisonCount prior periods, count (or sum $valueField over) every entity matching $filters within that period, plus one count per entry…
mailGraph($graph)Render one graph for an email report, computing its data on the fly if not already provided.
mailHistogram($data, $title, $unit)Create a histogram that will be sent by email from data received by this controller WARNING: only use the first series
mailTendency($data, $title, $unit, $diffUnit, $inverse)Create a tendency graph that will be sent by email from data received by this controller WARNING: only use the first data of each series
onRoute($route)
routeCollections($route)What routeCount can count: every collection of every repository this controller serves, with the entity class it holds and the url of its schema -- what a chart's configurator offers as a source.
routeCount($route)API endpoint wrapping getStatistics -- reads every parameter from the request's query string (Route::get), adds schema/collection links to the response, plus one export link per file fo…
routeDaysWithout($route)Get the number of days without any new entity: the "record" gap between two consecutive distinct $dateField values (capped at 6 months, to ignore a collection's very first entry having no prior dat…
routeExport($route)The statistics of routeCount, as a file: one line per series, one column per period, the current first.
routeExportData($route)Any chart's values as a file, the way routeExport writes statistics: what mmo-chart shows, posted, comes back as a CSV or an Xlsx file.
toDateTime($value)A date as a repository gives it: a PHP date, a storage's own date object (MongoDB's UTCDateTime has toDateTime()), or a date string.

Constants2

API_PARAMETERS_INFO

const API_PARAMETERS_INFO = ['parameters' => ['filters' => […4 items]]]

COUNT_PARAMETERS

The query parameters of routeCount and routeExport.

const COUNT_PARAMETERS = […10 items]

Properties1

query

protected ?MMO\core\entities\Query $query = null
Type
null | Query

Methods14

ExportLines($statistics)

staticStatisticController.php:295

The lines of a file holding statistics: one per series, its name first, then one value per period.

static function ExportLines(array $statistics): array
$statisticsarrayas getStatistics returns them
returnsarraymixed>>
StatisticController::ExportLines(['labels' => ['2026', '2025'], 'legend' => ['Total'], 'data' => [[3, 5]]]); // [['serie' => 'Total', 'p0' => 3, 'p1' => 5]]

getRouteCollection($route)

StatisticController.php:311

The repository and the collection a statistics route names.

protected function getRouteCollection(MMO\core\server\Route $route): array
$routeRoute
returnsarrayiCollection}
throwsif repository doesn't match a configured repository.

getRouteStatistics($route, $repository, $collection)

StatisticController.php:326
protected function getRouteStatistics( MMO\core\server\Route $route, MMO\core\interfaces\iRepository $repository, MMO\core\interfaces\iCollection $collection ): array
See
getStatistics, with every parameter read from the route's query string.
$routeRoute
$repositoryiRepository
$collectioniCollection
returnsarray

getStatistics($collection, $filters, $datasets, $valueField, $percentFilters, $from, $period, $comparisonCount, $comparisonPeriod, $colors, $dateField, $repository)

StatisticController.php:64

Build a chart dataset: for the current period and each of $comparisonCount prior periods, count (or sum $valueField over) every entity matching $filters within that period, plus one count per entry of $datasets (each further filtered on top of $filters) -- optionally expressed as a percentage of entities also matching $percentFilters instead of a raw count/sum.

function getStatistics( MMO\core\interfaces\iCollection|string $collection, array $filters = [], array $datasets = [], ?string $valueField = null, ?array $percentFilters = null, DateTimeInterface $from = new DateTime(), string $period = 'y', int $comparisonCount = 1, string $comparisonPeriod = 'y', string $colors = 'default', string $dateField = 'ctime', MMO\core\interfaces\iRepository|string $repository = 'main' ): array
$collectioniCollection | stringthe collection to use
$filters = []arrayUn objet contenant un filtre à appliquer sur toutes les données.
$datasets = []arrayUn objet contenant le titre du dataset et un filtre à appliquer.
$valueField = nullnull | stringLe nom du champ de valeur à additionner. Si null, un simple compte se réalise.
$percentFilters = nullnull | arrayUn objet contenant un filtre à appliquer sur les données à exprimer en pourcentage.
$from = new DateTime()DateTimeInterfaceLa date à partir de laquelle les données sont prises en compte. Par défaut la date actuelle.
$period = 'y'stringLa période de temps à utiliser. y(année),m(mois),w(semaine),d(jour).
$comparisonCount = 1intNombre de période à comparer.
$comparisonPeriod = 'y'stringLa période à utiliser pour la comparaison. y(année),m(mois),w(semaine),d(jour).
$colors = 'default'stringLe type de palette de couleurs
$dateField = 'ctime'stringLe nom du champ de date. Par défaut "ctime"
$repository = 'main'iRepository | string
returnsarraymmo-chart's config: [ 'labels' => {[string]} one label per period, the current one first, 'legend' => {[string]} one name per series: Total, then each of $datasets, 'data' => {[[int|float]]} one list of values per series (as many as labels), 'colors' => {[string]} one colour per series ]
throws
throws
throws

mailGraph($graph)

StatisticController.php:546

Render one graph for an email report, computing its data on the fly if not already provided.

function mailGraph(array &$graph): string
$grapharray
returnsstring
throwsif neither 'data' nor 'params' is given.

mailHistogram($data, $title, $unit)

StatisticController.php:431

Create a histogram that will be sent by email from data received by this controller WARNING: only use the first series

function mailHistogram(array $data, ?string $title = null, ?string $unit = null): string
$dataarraymmo-chart's config, as getStatistics returns it
$title = nullnull | stringthe title of the graph
$unit = nullnull | stringthe unit to use with each value
returnsstring

mailTendency($data, $title, $unit, $diffUnit, $inverse)

StatisticController.php:486

Create a tendency graph that will be sent by email from data received by this controller WARNING: only use the first data of each series

function mailTendency( array $data, ?string $title = null, ?string $unit = null, ?string $diffUnit = null, bool $inverse = false ): string
$dataarraymmo-chart's config, as getStatistics returns it
$title = nullnull | stringthe title of the graph
$unit = nullnull | stringthe unit to use with each value
$diffUnit = nullnull | stringthe unit to use for the difference. if null, no difference shown
$inverse = falsebool
returnsstring

routeCollections($route)

StatisticController.php:242

What routeCount can count: every collection of every repository this controller serves, with the entity class it holds and the url of its schema -- what a chart's configurator offers as a source.

#[MMO\core\attributes\APIRoute('GET|stats/collections.json', mediaTypes: ['json'])] function routeCollections(MMO\core\server\Route $route): array
$routeRoute
returnsarrayarray<string, array{class: string, schema: string}>> {repository: {collection: {class, schema}}}
GET stats/collections.json // {"main": {"clients": {"class": "App\\Client", "schema": ".../data/schemas.json#$defs/App\\Client"}}}

routeCount($route)

StatisticController.php:212

API endpoint wrapping getStatistics -- reads every parameter from the request's query string (Route::get), adds schema/collection links to the response, plus one export link per file format (routeExport), and returns them: the answer's data is mmo-chart's config.

#[MMO\core\attributes\APIRoute('GET|stats/count/{repository}/{collection}.json?dateField={?dateField}&valueField={?valueField}&from={?from}&period={?period}&datasets={?datasets}&filters={?filters}&years={?years}&colors={?colors}&percentFilters={?percentFilters}', ['parameters' => […10 items]], mediaTypes: ['json'])] function routeCount(MMO\core\server\Route $route): array
$routeRoute
returnsarray
throwsif repository doesn't match a configured repository (aDataController::$repositories).
To get the tendency of the entity personnes for the 2 last years, sorted by sex (man|woman): StatisticController::Url('routeCount', ['repository' => 'main', 'collection' => 'personnes', 'period' => '2y', 'dataset' => ['men' => ['sex' => 'man'], 'women' => ['sex' => 'woman']])

routeDaysWithout($route)

StatisticController.php:347

Get the number of days without any new entity: the "record" gap between two consecutive distinct $dateField values (capped at 6 months, to ignore a collection's very first entry having no prior date to compare against), plus the current gap since the latest entity of each $datasets filter.

#[MMO\core\attributes\APIRoute('GET|days-without/{repository}/{collection}.json?dateField={?dateField}&datasets={?datasets}', […1 item], mediaTypes: ['json'])] function routeDaysWithout(MMO\core\server\Route $route): array
$routeRoute
returnsarray

routeExport($route)

StatisticController.php:261

The statistics of routeCount, as a file: one line per series, one column per period, the current first.

#[MMO\core\attributes\APIRoute('GET|stats/export/{repository}/{collection}.{extension}?dateField={?dateField}&valueField={?valueField}&from={?from}&period={?period}&datasets={?datasets}&filters={?filters}&years={?years}&colors={?colors}&percentFilters={?percentFilters}', ['parameters' => […10 items]], mediaTypes: ['csv', 'xlsx'])] function routeExport(MMO\core\server\Route $route): array
$routeRoute
returnsarraythe lines (ExportLines)
throwsif repository doesn't match a configured repository.
StatisticController's answer links to it: `<.../stats/export/main/personnes.xlsx?period=2y>; rel="export"; title="xlsx"`

routeExportData($route)

StatisticController.php:278

Any chart's values as a file, the way routeExport writes statistics: what mmo-chart shows, posted, comes back as a CSV or an Xlsx file. A chart that reads routeCount gets its own export link instead.

#[MMO\core\attributes\APIRoute('POST|stats/export.{extension}', ['parameters' => ['extension' => […4 items]]], mediaTypes: ['csv', 'xlsx'])] function routeExportData(MMO\core\server\Route $route): array
$routeRoutethe body is mmo-chart's config: labels, legend and data
returnsarraythe lines (ExportLines)
POST stats/export.xlsx {"labels": ["2026", "2025"], "legend": ["Total"], "data": [[3, 5]]}

toDateTime($value)

StatisticController.php:411

A date as a repository gives it: a PHP date, a storage's own date object (MongoDB's UTCDateTime has toDateTime()), or a date string.

protected function toDateTime(mixed $value): ?DateTimeImmutable
$valuemixed
returnsnull | DateTimeImmutablenull when it is not a date
Inherited members (61)
ConstantsDeclared byDescription
ALERTiLogLevelAlert: Action must be taken immediately.
ALLiLogLevelMask for all
ALL_PLUSiLogLeveldebug everything
BACKTRACEiLogLevel
COLORSaDataControllerNamed color palettes for chart/report endpoints, e.g.
CRITICALiLogLevelCritical: Critical conditions.
DEBUGiLogLevelDebug: Detailed debug information.
DEEPiLogLeveldebug everything with arguments
EMERGENCYiLogLevelEmergency: System is unusable.
ERRORiLogLevelError: Runtime errors that do not require immediate action but should typically be logged and monitored.
ERROR_PLUSiLogLevelMask for all error
FATALiLogLevelMask for all fatal error
INFOiLogLevelInfo: Interesting events.
INFO_PLUSiLogLevelMask for all errors/warning/notices/info
MEMORYiLogLevelinclude backtrace object
NOTICEiLogLevelNotice: Normal but significant events.
NOTICE_PLUSiLogLevelMask for all errors/warning/notices/info
TRACEiLogLevelinclude backtrace as origins
WARNINGiLogLevelWarning: Exceptional occurrences that are not errors.
WARNING_PLUSiLogLevelMask for all errors and warnings
PropertiesDeclared byDescription
repositoriesaDataController
verbosityaModule
MethodsDeclared byDescription
alert($message, $context)aModuleAction must be taken immediately.
backtraces($message, $context)aModuleForce-log a message at INFO level together with a full stack trace/memory snapshot (iLogLevel::BACKTRACE), bypassing the verbosity mask entirely (any non-zero verbosity is enough to trigger it).
Configure($config)aModuleMerge $config into this module's configuration in the shared Configuration.
critical($message, $context)aModuleCritical conditions. No-op if CRITICAL is currently filtered out by the verbosity mask.
debug($message, $context)aModuleDetailed debug information.
debugName()aModuleHuman-readable identifier used as the default {origin} in this instance's log messages.
DefaultConfig()aModuleThe config a module starts from before anything loaded via Configuration is merged in.
emergency($message, $context)aModuleSystem is unusable. No-op if EMERGENCY is currently filtered out by the verbosity mask.
error($message, $context)aModuleRuntime errors that do not require immediate action but should typically be logged and monitored.
ExtractRoutes($config)aControllerExtract all the APIRoute from this class, regardless of the fact they are registered in the application
getMainRepository()aDataController
getRepositories()aDataController
getRepository($repo)aDataController
getVerbosity()aModule
HasRoutes()aControllerTells if any APIRoute are registered in the application
info($message, $context)aModuleInteresting events. No-op if INFO is currently filtered out by the verbosity mask.
Instantiate($config)aModule(Re)create the shared instance immediately, configured with $config merged into the class's existing config.
IsShared()aModule
Link($method, $data)aControllerBuild a Link (URL + label/icon/description/title, taken from the route's APIRoute metadata) for a registered route.
log($level, $message, $context)aModuleLogs with an arbitrary level.
notice($message, $context)aModuleNormal but significant events.
offsetExists($key)aModule\ArrayAccess::offsetExists -- tells whether $key is a declared property at all, regardless of whether it is currently readable/writable through array access.
offsetGet($key)aModule\ArrayAccess::offsetGet -- reads a property directly, unless it has a #[Serialize(serialize: 'methodName')] attribute, in which case that zero-argument method is called instead (e.g.
offsetSet($key, $value)aModule\ArrayAccess::offsetSet -- sets a property directly, unless it has a #[Serialize(unserialize: 'methodName')] attribute, in which case that method is called with $value instead (e.g.
offsetUnset($key)aModule\ArrayAccess::offsetUnset -- equivalent to $module[$key] = null.
Route($method)aControllerGet a registered APIRoute WARNING: if this class was not registered in the application, it will return null.
Routes()aControllerGet a list of registered APIRoutes (from this class) in the application WARNING: if this class was not registered in the application, it will return an empty array.
serialize($path, $serializer)aModule\MMO\core\interfaces\iSerializable::serialize -- exports every property (via
setRepositories($repositories)aDataControllerRegister the repositories this controller can query, keyed by name (e.g.
setVerbosity($verb)aModuleSet the current verbosity mask, replacing whatever it was before.
Shared()aModuleGet the shared instance, creating (and configuring) it on first call.
testVerbosity($level)aModuleTest whether a given level (or set of levels) is currently enabled by this instance's verbosity mask.
traitUnshare($serialize)aModuleRemove the shared instance, so the next Shared() call creates a fresh one.
unserialize($data, $path, $instances)aModule\MMO\core\interfaces\iSerializable::unserialize -- merges $data onto DefaultConfig, runs it through the (possibly overridden) constructor, then applies each resulting value to its property (via
Unshare($serialize)aModuleLike tSingleton::Unshare, but also disconnects this module's instance from every event it was listening to (via Event::DisconnectListeners) before dropping it.
Url($method, $data)aControllerBuild the URL for a registered route, filling in any {placeholder} from $data.
warning($message, $context)aModuleExceptional occurrences that are not errors.
__construct($config)aModuleprotected constructor, so it is not possible to do new Module(...) called by the serialization process just before being injected with data.
__wakeup()aControllerCalled automatically right after the controller's shared instance is built (aModule::unserialize): registers onRoute as a Route listener with a low priority (-10), so it…