PHPackages                             andrewdyer/metrics - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. andrewdyer/metrics

ActiveLibrary[Database &amp; ORM](/categories/database)

andrewdyer/metrics
==================

A metrics library for Eloquent applications that calculates strongly typed aggregates over a configurable date range

1.0.2(1mo ago)00[1 issues](https://github.com/andrewdyer/metrics/issues)MITPHPPHP ^8.3CI passing

Since Jun 2Pushed 1mo ago1 watchersCompare

[ Source](https://github.com/andrewdyer/metrics)[ Packagist](https://packagist.org/packages/andrewdyer/metrics)[ Docs](https://github.com/andrewdyer/metrics)[ RSS](/packages/andrewdyer-metrics/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (4)Dependencies (6)Versions (6)Used By (0)

Metrics
=======

[](#metrics)

A metrics library for Eloquent applications that calculates strongly typed aggregates over a configurable date range.

[![Latest Stable Version](https://camo.githubusercontent.com/dcecd84f67c7427dfd6e6b27f2334c7bddac9934d6f60ea83871119cdeb63f3f/687474703a2f2f706f7365722e707567782e6f72672f616e64726577647965722f6d6574726963732f763f7374796c653d666c61742d737175617265)](https://packagist.org/packages/andrewdyer/metrics)[![Total Downloads](https://camo.githubusercontent.com/c2f787a80f98606b8bd0e52584317b816742830cf60f5b30c5153971d909c055/687474703a2f2f706f7365722e707567782e6f72672f616e64726577647965722f6d6574726963732f646f776e6c6f6164733f7374796c653d666c61742d737175617265)](https://packagist.org/packages/andrewdyer/metrics)[![License](https://camo.githubusercontent.com/f02d919508c9d03200deb2a18d51ec547c9414c522f352bcc6fa085fe20fd5b0/687474703a2f2f706f7365722e707567782e6f72672f616e64726577647965722f6d6574726963732f6c6963656e73653f7374796c653d666c61742d737175617265)](https://packagist.org/packages/andrewdyer/metrics)[![PHP Version Require](https://camo.githubusercontent.com/a7e5f5e5f2e12c245993f584d1dc86b0b4b3fad6dbd23451518cd501ff83fead/687474703a2f2f706f7365722e707567782e6f72672f616e64726577647965722f6d6574726963732f726571756972652f7068703f7374796c653d666c61742d737175617265)](https://packagist.org/packages/andrewdyer/metrics)

Introduction
------------

[](#introduction)

This library provides Eloquent-based metric calculations, returning strongly typed result objects for scalar aggregates, time-grouped trends with zero-filled gaps, and categorical breakdowns, all with JSON serialisation support. It works in any PHP application with an Eloquent connection, including outside Laravel via the Capsule manager.

Prerequisites
-------------

[](#prerequisites)

- **[PHP](https://www.php.net/)**: Version 8.3 or higher is required.
- **[Composer](https://getcomposer.org/)**: Dependency management tool for PHP.
- **[Eloquent ORM](https://laravel.com/docs/eloquent)**: An configured Eloquent connection is required.

Installation
------------

[](#installation)

```
composer require andrewdyer/metrics
```

Getting Started
---------------

[](#getting-started)

### 1. Set up Eloquent

[](#1-set-up-eloquent)

This library requires an Eloquent connection. In a Laravel application this is configured automatically. Outside of Laravel, bootstrap Eloquent using the Capsule manager:

```
use Illuminate\Database\Capsule\Manager as Capsule;

$capsule = new Capsule();

$capsule->addConnection([
    'driver'   => 'mysql',
    'host'     => '127.0.0.1',
    'database' => 'my_database',
    'username' => 'root',
    'password' => '',
    'charset'  => 'utf8',
    'prefix'   => '',
]);

$capsule->setAsGlobal();
$capsule->bootEloquent();
```

### 2. Create a metric

[](#2-create-a-metric)

Extend `Value` and implement `calculate()` to define the query, and `getStartDate()` / `getEndDate()` to define the date range:

```
use AndrewDyer\Metrics\Value;
use AndrewDyer\Metrics\Results\ValueResult;
use DateTimeImmutable;

class TotalNewUsers extends Value
{
    public function calculate(): ValueResult
    {
        return $this->count(User::query());
    }

    public function getStartDate(): DateTimeImmutable
    {
        return new DateTimeImmutable('first day of this month');
    }

    public function getEndDate(): DateTimeImmutable
    {
        return new DateTimeImmutable('last day of this month');
    }
}
```

Usage
-----

[](#usage)

### Calculating a metric

[](#calculating-a-metric)

Call `calculate()` on any metric instance to run the query and return a typed result:

```
$metric = new TotalNewUsers();
$result = $metric->calculate();

$result->getResult(); // e.g. 42
```

### JSON serialisation

[](#json-serialisation)

All result objects implement `JsonSerializable`:

```
json_encode($metric->calculate());
// {"result": 42}
```

### Customising the metric name and description

[](#customising-the-metric-name-and-description)

Override `getName()` and `getDescription()` on any metric to provide a human-readable label:

```
public function getName(): string
{
    return 'Total New Users';
}

public function getDescription(): string
{
    return 'The total number of users who signed up this month.';
}
```

### Rounding

[](#rounding)

Override `getRoundingPrecision()` and `getRoundingMode()` to control how aggregate values are rounded:

```
public function getRoundingPrecision(): int
{
    return 2;
}

public function getRoundingMode(): int
{
    return PHP_ROUND_HALF_UP;
}
```

Metric Types
------------

[](#metric-types)

### Value

[](#value)

A `Value` metric calculates a single aggregate over a date range. When the model uses timestamps, the date filter defaults to `created_at`. When the model does not use timestamps, no date filter is applied unless an explicit `$dateColumn` is passed.

#### Count

[](#count)

Returns the number of records within the date range:

```
$this->count(User::query());
```

To filter by a specific date column, pass it explicitly:

```
$this->count(User::query(), null, 'published_at');
```

On models without timestamps, no date filter is applied unless an explicit column is given:

```
$this->count(Product::query()); // no date filter — all records returned
$this->count(Product::query(), null, 'released_at'); // filtered by released_at
```

#### Sum

[](#sum)

Totals the values of a column across all matching records:

```
$this->sum(User::query(), 'revenue');
```

#### Average

[](#average)

Calculates the mean value of a column across all matching records:

```
$this->average(User::query(), 'revenue');
```

#### Minimum

[](#minimum)

Returns the smallest value of a column within the date range:

```
$this->min(User::query(), 'revenue');
```

#### Maximum

[](#maximum)

Returns the largest value of a column within the date range:

```
$this->max(User::query(), 'revenue');
```

### Trend

[](#trend)

A `Trend` metric calculates an aggregate grouped over time at a given frequency. Missing dates within the range are automatically filled with zero values.

When the model uses timestamps, the date column defaults to `created_at`. When the model does not use timestamps, an explicit `$dateColumn` must be passed or an `InvalidArgumentException` is thrown.

Extend `Trend` and implement `calculate()`, `getStartDate()`, `getEndDate()`, and `getFrequency()`:

```
use AndrewDyer\Metrics\Trend;
use AndrewDyer\Metrics\Enums\Frequency;
use AndrewDyer\Metrics\Results\TrendResult;
use DateTimeImmutable;

class UserSignupsOverTime extends Trend
{
    public function calculate(): TrendResult
    {
        return $this->count(User::query(), null, 'created_at');
    }

    public function getFrequency(): Frequency
    {
        return Frequency::Monthly;
    }

    public function getStartDate(): DateTimeImmutable
    {
        return new DateTimeImmutable('first day of january this year');
    }

    public function getEndDate(): DateTimeImmutable
    {
        return new DateTimeImmutable('last day of december this year');
    }
}
```

Calculating the metric returns a keyed array of labels to aggregate values:

```
$metric = new UserSignupsOverTime();
$result = $metric->calculate();

$result->getResult(); // ['January 2026' => 12, 'February 2026' => 9, ...]
```

#### Supported frequencies

[](#supported-frequencies)

```
Frequency::Daily
Frequency::Weekly
Frequency::Monthly
```

#### Count

[](#count-1)

Tracks how many records fall within each period of the date range:

```
$this->count(User::query(), null, 'created_at');
```

#### Sum

[](#sum-1)

Totals a column's values for each period across the date range:

```
$this->sum(User::query(), 'revenue', 'created_at');
```

#### Average

[](#average-1)

Calculates the mean value of a column for each period in the range:

```
$this->average(User::query(), 'revenue', 'created_at');
```

#### Minimum

[](#minimum-1)

Returns the lowest value of a column recorded within each period:

```
$this->min(User::query(), 'revenue', 'created_at');
```

#### Maximum

[](#maximum-1)

Returns the highest value of a column recorded within each period:

```
$this->max(User::query(), 'revenue', 'created_at');
```

### Partition

[](#partition)

A `Partition` metric calculates an aggregate grouped by a categorical column, returning a key-value breakdown. Results are filtered to the configured date range.

When the model uses timestamps, the date filter defaults to `created_at`. When the model does not use timestamps, no date filter is applied unless an explicit `$dateColumn` is passed.

Extend `Partition` and implement `calculate()`, `getStartDate()`, and `getEndDate()`:

```
use AndrewDyer\Metrics\Partition;
use AndrewDyer\Metrics\Results\PartitionResult;
use DateTimeImmutable;

class UsersByCountry extends Partition
{
    public function calculate(): PartitionResult
    {
        return $this->count(User::query(), 'country');
    }

    public function getStartDate(): DateTimeImmutable
    {
        return new DateTimeImmutable('first day of this year');
    }

    public function getEndDate(): DateTimeImmutable
    {
        return new DateTimeImmutable('today');
    }
}
```

Calculating the metric returns a keyed array of group labels to aggregate values:

```
$metric = new UsersByCountry();
$result = $metric->calculate();

$result->getResult(); // ['GB' => 42, 'US' => 31, 'DE' => 14]
```

#### Count

[](#count-2)

Groups records by a column and counts how many fall into each category:

```
$this->count(User::query(), 'country');
```

To filter by a specific date column, pass it explicitly:

```
$this->count(User::query(), 'country', null, 'registered_at');
```

#### Sum

[](#sum-2)

Groups records by a column and totals a second column within each group:

```
$this->sum(User::query(), 'country', 'revenue');
```

#### Average

[](#average-2)

Groups records by a column and calculates the mean of a second column per group:

```
$this->average(User::query(), 'country', 'revenue');
```

Database Support
----------------

[](#database-support)

The following database drivers are supported:

DriverMinimum VersionMySQL5.7+MariaDB10.2+SQLite3.38.0+PostgreSQL9.4+License
-------

[](#license)

Licensed under the [MIT licence](https://opensource.org/licenses/MIT) and is free for private or commercial projects.

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance92

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity52

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~4 days

Total

4

Last Release

38d ago

Major Versions

0.1.0 → 1.0.02026-06-08

### Community

Maintainers

![](https://www.gravatar.com/avatar/666597ea6e46748a89fe8764d1a45b4d0da97daf1bb1e9770ea34ae41f706d08?d=identicon)[andrewdyer](/maintainers/andrewdyer)

---

Top Contributors

[![andrewdyer](https://avatars.githubusercontent.com/u/8114523?v=4)](https://github.com/andrewdyer "andrewdyer (39 commits)")

---

Tags

aggregationeloquentmetricspartitionphptrendvaluephpeloquentMetricsvalueaggregationPartitiontrend

###  Code Quality

TestsPHPUnit

Code StylePHP CS Fixer

### Embed Badge

![Health badge](/badges/andrewdyer-metrics/health.svg)

```
[![Health](https://phpackages.com/badges/andrewdyer-metrics/health.svg)](https://phpackages.com/packages/andrewdyer-metrics)
```

###  Alternatives

[mongodb/laravel-mongodb

A MongoDB based Eloquent model and Query builder for Laravel

7.1k8.4M98](/packages/mongodb-laravel-mongodb)[kirschbaum-development/eloquent-power-joins

The Laravel magic applied to joins.

1.6k32.6M46](/packages/kirschbaum-development-eloquent-power-joins)[spiritix/lada-cache

A Redis based, automated and scalable database caching layer for Laravel

592456.3k2](/packages/spiritix-lada-cache)[glushkovds/phpclickhouse-laravel

Adapter of the most popular library https://github.com/smi2/phpClickHouse to Laravel

2051.5M2](/packages/glushkovds-phpclickhouse-laravel)[io238/laravel-iso-countries

Ready-to-use Laravel models and relations for country (ISO 3166), language (ISO 639-1), and currency (ISO 4217) information with multi-language support.

5771.9k](/packages/io238-laravel-iso-countries)[sebastiaanluca/laravel-boolean-dates

Automatically convert Eloquent model boolean attributes to dates (and back).

40117.0k1](/packages/sebastiaanluca-laravel-boolean-dates)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
