sakanjo/laravel-easy-metrics
Laravel package to quickly build app metrics (value, trend, bar, line, pie, doughnut, polar). Supports ranges, aggregates (count/sum/min/max/avg), and growth rates. Designed to work with Laravel and Filament widgets for dashboards.
Installation: Add the package via Composer:
composer require sakanjo/laravel-easy-metrics
First Use Case: Create a simple metric in a Filament widget or a Laravel command. For example, to display a user count trend in a Filament widget:
use SaKanjo\EasyMetrics\Metrics\Trend;
use App\Models\User;
[$labels, $data] = Trend::make(User::class)
->range(30)
->countByMonths();
Quick Value Metric: For a single KPI like total users:
use SaKanjo\EasyMetrics\Metrics\Value;
$totalUsers = Value::make(User::class)
->count();
Explore Metrics: Test other metric types like Doughnut for categorical breakdowns or Bar for time-series comparisons.
Value::make(User::class)
->sum('revenue')
->range(30);
[$labels, $data] = Doughnut::make(User::class)
->count('status');
[$labels, $data] = Trend::make(User::class)
->countByMonths();
Pass a second column to aggregate functions to group results:
// Group users by gender and count
[$labels, $data] = Doughnut::make(User::class)
->count('gender');
// Group revenue by region
[$labels, $data] = Doughnut::make(Order::class)
->sum('amount', 'region');
Configure time ranges for trends or comparisons:
// Fixed range (30 days)
Trend::make(User::class)
->range(30)
->countByDays();
// Custom ranges (e.g., 15 days, 30 days, all time)
Trend::make(User::class)
->ranges([15, 30, Range::ALL])
->countByMonths();
// Predefined ranges (e.g., today, yesterday, month-to-date)
Trend::make(User::class)
->ranges([Range::TODAY, Range::YESTERDAY, Range::MTD])
->countByDays();
Add growth metrics to any metric type:
// Value metric with growth rate
[$value, $growth] = Value::make(User::class)
->withGrowthRate()
->growthRateType(GrowthRateType::Percentage)
->count();
// Trend metric with growth rate
[$labels, $data, $growth] = Trend::make(User::class)
->withGrowthRate()
->countByMonths();
Use the package directly in Filament widgets for dashboards:
use SaKanjo\EasyMetrics\Metrics\Trend;
use Filament\Widgets\ChartWidget;
class UsersTrendWidget extends ChartWidget {
protected function getData(): array {
[$labels, $data] = Trend::make(User::class)
->range($this->filter)
->countByMonths();
return [
'datasets' => [[
'label' => 'Users',
'data' => $data,
]],
'labels' => $labels,
];
}
protected function getType(): string {
return 'line';
}
}
Specify a custom date column for time-based metrics:
Trend::make(Order::class)
->dateColumn('created_at') // Defaults to 'created_at'
->countByWeeks();
Zero Division in Growth Rates: Growth rates may fail if the baseline value (e.g., previous period) is zero. Handle this in your UI or add a check:
if ($growth->previousValue === 0) {
$growth->percentageChange = 0;
}
Database Engine Quirks:
Time truncation (e.g., BY MONTH, BY DAY) may behave differently across databases (MySQL vs. PostgreSQL). Test thoroughly if using mixed environments.
Performance with Large Datasets:
Aggregations like countByMonths() on tables with millions of records can be slow. Optimize with database indexes or limit ranges:
Trend::make(User::class)
->range(30) // Limit to 30 days
->countByDays();
Enum Labels Require EasyEnum:
To use getLabel() for enum-based metrics, ensure your enum uses SaKanjo\EasyEnum:
enum UserStatus: int {
use EasyEnum;
case Active = 1;
case Inactive = 0;
}
Default Date Column:
All time-based metrics default to created_at. Override with dateColumn() if your model uses a different column (e.g., published_at).
Inspect Raw Queries: Enable Laravel query logging to debug complex aggregations:
DB::enableQueryLog();
$data = Trend::make(User::class)->countByMonths();
dd(DB::getQueryLog());
Check Ranges:
Verify time ranges with dd() before rendering:
$metric = Trend::make(User::class)->range(30);
dd($metric->getRange());
Growth Rate Edge Cases: Log growth calculations to catch unexpected values:
[$value, $growth] = Value::make(User::class)
->withGrowthRate()
->count();
logger()->info('Growth rate', [
'current' => $value,
'previous' => $growth->previousValue,
'percentage' => $growth->percentageChange,
]);
Custom Metric Classes:
Extend existing metrics (e.g., Trend) to add domain-specific logic:
class RevenueTrend extends Trend {
public function revenueByMonths() {
return $this->sumByMonths('amount');
}
}
Override Growth Calculations:
Modify growth rate logic by extending the GrowthRate class or using middleware:
// Example: Custom growth rate logic
$growth = $metric->withGrowthRate()
->customGrowthLogic(function ($current, $previous) {
return $current - $previous; // Absolute difference
});
Add Custom Ranges:
Extend the Range enum for project-specific time periods:
enum CustomRange {
use \SaKanjo\EasyMetrics\Enums\Range;
case QUARTERLY;
}
Integrate with Caching: Cache metric results to improve performance:
$cacheKey = 'users_trend_' . $range;
return Cache::remember($cacheKey, now()->addHours(1), function () use ($range) {
return Trend::make(User::class)
->range($range)
->countByMonths();
});
Use rangesFromOptions in Filament:
Dynamically generate range options for user-selectable time periods:
protected function getFilters(): array {
return [
7 => 'Last 7 Days',
30 => 'Last 30 Days',
Range::MTD => 'Month-to-Date',
Range::YTD => 'Year-to-Date',
];
}
// In widget:
Trend::make(User::class)
->rangesFromOptions($this->getFilters())
->countByDays();
Combine Metrics for Dashboards: Use multiple metrics in a single widget for richer insights:
[$users, $growth] = Value::make(User::class)
->withGrowthRate()
->count();
[$activeUsers, $inactiveUsers] = Doughnut::make(User::class)
->count('status');
Localization: Localize labels and growth rate messages:
__("Growth: :percentage%", ['percentage' => $growth->percentageChange]);
Testing: Mock metrics in tests using Laravel's query builder:
$metric = Mockery::mock(Trend::class);
$metric->shouldReceive('countByMonths')
->andReturn([['Jan',
How can I help you explore Laravel packages today?