Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Easy Metrics Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

Start by installing the package via Composer:

composer require sakanjo/laravel-easy-metrics

First Use Case: Create a simple Value metric to display a single KPI (e.g., total users) in a Filament widget or Blade view:

use SaKanjo\EasyMetrics\Metrics\Value;
use App\Models\User;

// In a Filament widget or controller
$totalUsers = Value::make(User::class)->count();

Where to Look First:

  • Documentation: Focus on the Usage section in the README for metric types (Value, Doughnut, Trend, etc.).
  • Practical Examples: The Filament Widgets section provides ready-to-use code snippets for integrating metrics into dashboards.
  • Growth Rates: Explore the Growth Rates section to add comparative metrics (e.g., "20% increase from last month") to your visualizations.

Implementation Patterns

1. Metric Type Selection

Use the appropriate metric class based on your visualization needs:

  • Single KPIs: Value (e.g., ->sum('revenue'), ->count()).
  • Categorical Breakdowns: Doughnut or Pie (e.g., ->count('status') for user status distribution).
  • Time-Series Data: Trend, Line, or Bar (e.g., ->countByMonths(), ->averageByWeeks('order_value')).

Example: Time-Series Trend

use SaKanjo\EasyMetrics\Metrics\Trend;
use App\Models\Order;

[$labels, $data] = Trend::make(Order::class)
    ->range(30)
    ->sumByMonths('total');

2. Flexible Grouping

Pass a second column to aggregate functions for grouped metrics:

// Group users by gender and count
[$labels, $data] = Doughnut::make(User::class)
    ->count('gender');

// Group orders by status and calculate average value
[$labels, $data] = Doughnut::make(Order::class)
    ->average('total', 'status');

3. Time Range Configuration

Chain range methods to control the time window for metrics:

// Fixed range (30 days)
Trend::make(Order::class)
    ->range(30)
    ->countByDays();

// Custom ranges (e.g., 15 days, 30 days, all time)
Trend::make(Order::class)
    ->ranges([15, 30, Range::ALL])
    ->countByMonths();

// Predefined ranges (e.g., Month-to-Date)
Trend::make(Order::class)
    ->range(Range::MTD)
    ->sumByDays('revenue');

4. Growth Rates

Add comparative metrics to visualize growth:

// Value metric with growth rate (percentage)
[$value, $growth] = Value::make(Order::class)
    ->withGrowthRate()
    ->growthRateType(GrowthRateType::Percentage)
    ->count();

// Trend metric with growth rate
[$labels, $data, $growth] = Trend::make(Order::class)
    ->withGrowthRate()
    ->sumByMonths('revenue');

5. Filament Integration

Use metrics directly in Filament widgets for dashboards:

use Filament\Widgets\ChartWidget;
use SaKanjo\EasyMetrics\Metrics\Trend;

class RevenueTrendWidget extends ChartWidget {
    protected function getData(): array {
        [$labels, $data] = Trend::make(Order::class)
            ->range($this->filter)
            ->sumByMonths('total');

        return [
            'datasets' => [['label' => 'Revenue', 'data' => $data]],
            'labels' => $labels,
        ];
    }

    protected function getType(): string {
        return 'line';
    }
}

6. Custom Date Columns

Specify a custom date column for time-based metrics:

Trend::make(Order::class)
    ->dateColumn('created_at') // Defaults to 'created_at'
    ->countByWeeks();

7. Enum Labels

Use SaKanjo\EasyEnum to transform database values into human-readable labels:

// In your enum
enum UserStatus: int {
    use EasyEnum;

    case Active = 0;
    case Inactive = 1;
}

// In your metric
[$labels, $data] = Doughnut::make(User::class)
    ->count('status'); // Labels auto-converted via getLabel()

Gotchas and Tips

Pitfalls

  1. 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->percentage = 0;
    }
    
  2. Time Zone Mismatches: Time-based metrics (e.g., ByMonths, ByDays) assume the database uses UTC. If your app uses a different timezone, explicitly set it:

    Trend::make(Order::class)
        ->dateColumn('created_at')
        ->setTimezone('America/New_York')
        ->countByDays();
    
  3. Performance with Large Datasets: Time-series metrics (e.g., countByMonths()) can be slow for tables with millions of records. Optimize with:

    • Database indexes on date columns.
    • Caching (e.g., Laravel's cache or Redis) for frequently accessed metrics.
    • Pre-aggregating data in a separate table for complex queries.
  4. Custom Ranges and Filament: When using rangesFromOptions() in Filament, ensure the range values match the metric's expected format (e.g., integers for days, Range::* constants for predefined ranges).

  5. Database Engine Compatibility: While the package supports multiple database engines, some time-truncation functions (e.g., DATE_TRUNC) may behave differently in PostgreSQL vs. MySQL. Test thoroughly if switching databases.

Debugging Tips

  • Query Inspection: Use Laravel's query logging to inspect the generated SQL:

    \DB::enableQueryLog();
    $result = Value::make(User::class)->count();
    \Log::info(\DB::getQueryLog());
    
  • Label Customization: Override default labels by passing a closure to getLabel():

    Doughnut::make(User::class)
        ->count('status')
        ->getLabel(function ($value) {
            return match ($value) {
                0 => 'Active Users',
                1 => 'Inactive Users',
                default => 'Unknown',
            };
        });
    
  • Growth Rate Types: Ensure you use the correct GrowthRateType for your use case:

    • GrowthRateType::Value: Absolute change (e.g., "+100 users").
    • GrowthRateType::Percentage: Relative change (e.g., "+20%").

Extension Points

  1. 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');
        }
    }
    
  2. Dynamic Ranges: Create a dynamic range resolver for Filament filters:

    public function getFilters(): array {
        return collect([15, 30, 60, 90])
            ->mapWithKeys(fn ($days) => [$days => "Last $days Days"])
            ->toArray();
    }
    
  3. Caching Layer: Add caching to metrics for performance:

    use Illuminate\Support\Facades\Cache;
    
    function getCachedMetric() {
        return Cache::remember('users_count', now()->addHours(1), function () {
            return Value::make(User::class)->count();
        });
    }
    
  4. Testing: Mock metrics in tests using Laravel's query builder:

    $mock = Mockery::mock('overload', User::class);
    $mock->shouldReceive('count')->andReturn(100);
    $result = Value::make(User::class)->count(); // Returns 100
    

Configuration Quirks

  • Default Date Column: The package defaults to created_at. Override it explicitly if your model uses a different column (e.g., updated_at):

    Trend::make(Order::class)
        ->dateColumn('updated_at')
        ->countByDays();
    
  • Range Validation: Ensure range values are valid (e.g., positive integers for days). The package does not validate ranges by default.

  • Growth Rate Precision: Growth rates are calculated with floating-point arithmetic, which may introduce rounding errors. Format the output to 2 decimal places for consistency:

    number_format($growth->percentage,
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
calmfox/watch-sylius
damienfern/grpc-symfony-bundle
atoolo/index-bundle
atoolo/genai-bundle
coprotoai/laravel-ticket
davidjln/llm-carbon-bundle
cryonighter/valid-request-bundle
coolms/taxonomy-bundle
coolms/field-bundle
articulate-orm/symfony
aaix/laravel-tall-architect
ephoto/akeneo-connector
emmanuelballery/eb-plantumlbundle
emielburgman/symfony-visitor-beacon
emielburgman/symfony-visit-storage
emielburgman/symfony-security-headers
emielburgman/symfony-log-viewer
emarref/xdebug-bundle
emarref/pubnub-bundle
elriseio/finance-money-bundle