Aller au contenu

Sources

phil/reporting-bundle

Une source est la façon dont un bundle, ou l'application, place un chiffre sur un tableau de bord. Le bundle ne connaît aucun chiffre qui lui soit propre.

Une mesure

use Phil\ReportingBundle\Application\Contract\MetricSource;
use Phil\ReportingBundle\Domain\Format\MetricFormat;
use Phil\ReportingBundle\Domain\ValueObject\Metric;
use Phil\ReportingBundle\Domain\ValueObject\Period;

final readonly class Revenue implements MetricSource
{
    public function __construct(private Payments $payments) {}

    public static function key(): string
    {
        return 'revenue';
    }

    public function title(): string
    {
        return 'Revenue';
    }

    public function metric(Period $period): Metric
    {
        return new Metric($this->payments->sumBetween($period->start, $period->end), MetricFormat::Currency, 'CAD', 2);
    }
}

Une Metric est un nombre fini, son MetricFormat — Number, Percent pour une part de un, Currency avec son code ISO 4217 —, et de 0 à 6 décimales. Un widget avec compare interroge la source une deuxième fois, pour la période précédente, et affiche la variation sous forme de direction et de pourcentage du chiffre précédent ; aucune variation quand le chiffre précédent est nul.

Séries

use Phil\ReportingBundle\Application\Contract\SeriesSource;
use Phil\ReportingBundle\Domain\ValueObject\Period;
use Phil\ReportingBundle\Domain\ValueObject\Series;
use Phil\ReportingBundle\Domain\ValueObject\SeriesSet;

final readonly class Visits implements SeriesSource
{
    public function __construct(private VisitLog $log) {}

    public static function key(): string
    {
        return 'visits';
    }

    public function title(): string
    {
        return 'Visits';
    }

    public function series(Period $period): SeriesSet
    {
        $days = $period->split('1 day');

        return new SeriesSet(
            array_map(static fn (Period $day): string => $day->start->format('M j'), $days),
            [new Series('Visits', array_map(fn (Period $day): ?float => $this->log->countBetween($day->start, $day->end), $days))],
        );
    }
}

Un SeriesSet contient des libellés et des séries, chaque série ayant une valeur par libellé, null là où elle n'en a pas. Il est borné à 500 libellés, 24 séries et des libellés de 200 caractères, les limites de phil/svg-chart : une source ne coûte ainsi pas plus à dessiner qu'un grand graphique honnête.

Périodes

Une Period va de son début inclus à sa fin exclue. contains() dit si un moment y tombe ; split('1 day'), split('1 week') ou split('1 month') la découpe en tranches consécutives, la dernière coupée à la fin, au plus 500. Un pas qui n'avance pas est refusé avec InvalidFigures.

Les périodes prédéfinies sont comptées en journées entières du fuseau horaire configuré, à partir du service clock :

Période prédéfiniePériodeLa période précédente
today, yesterdayLe jourLe jour d'avant
last_7_days, last_30_daysLes jours qui se terminent aujourd'hui, aujourd'hui comprisLe même nombre de jours avant
this_month, last_monthLe mois civilLe mois d'avant
last_12_monthsDouze mois civils qui se terminent par celui-ciLes douze d'avant
this_year, last_yearL'année civileL'année d'avant

Enregistrement

Avec l'autoconfiguration, un service qui implémente MetricSource ou SeriesSource reçoit l'étiquette phil_reporting.metric_source ou phil_reporting.series_source, et il est trouvé par la clé que renvoie sa key(). Une étiquette qui nomme sa propre key l'emporte, ce qui enregistre une même classe sous plusieurs clés :

services:
    app.revenue_eur:
        class: App\Reporting\Revenue
        tags: [{ name: phil_reporting.metric_source, key: revenue_eur }]

Une source n'est construite que lorsqu'un widget la demande. Elle ne garde aucun état d'une requête à l'autre : elle lit ce qu'elle mesure chaque fois qu'on l'interroge.