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 Stats Laravel Package

spatie/laravel-stats

Lightweight Laravel package to track and summarize database stat changes over time. Create a stats class, call increase/decrease on events, then query totals and deltas over ranges grouped by day/week/month for easy reporting.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation

    composer require spatie/laravel-stats
    php artisan vendor:publish --provider="Spatie\Stats\StatsServiceProvider" --tag="stats-migrations"
    php artisan migrate
    
  2. Create a Stats Class Define a class extending Spatie\Stats\BaseStats:

    // app/Stats/SubscriptionStats.php
    namespace App\Stats;
    
    use Spatie\Stats\BaseStats;
    
    class SubscriptionStats extends BaseStats {}
    
  3. First Use Case Track subscriptions/cancellations in a controller or event:

    // When a user subscribes
    SubscriptionStats::increase();
    
    // When a user cancels
    SubscriptionStats::decrease();
    
  4. Query Stats Fetch historical data (e.g., weekly stats for the last 2 months):

    $stats = SubscriptionStats::query()
        ->start(now()->subMonths(2))
        ->end(now())
        ->groupByWeek()
        ->get();
    

Implementation Patterns

Core Workflows

  1. Event-Driven Tracking

    • Use increase()/decrease() in:
      • Controllers (e.g., after subscription confirmation).
      • Observers/Events (e.g., SubscriptionCreated event).
      • Jobs (e.g., ProcessPaymentJob after successful payment).
    • Example:
      // app/Events/SubscriptionCreated.php
      public function handle() {
          SubscriptionStats::increase();
      }
      
  2. Batch Updates

    • For external data (e.g., API syncs), use set():
      $count = $api->fetchSubscriptionCount();
      SubscriptionStats::set($count);
      
  3. Time-Adjusted Tracking

    • Record past events with timestamps:
      SubscriptionStats::increase(1, $subscription->created_at);
      
  4. Query Patterns

    • Time Ranges: Filter by start()/end() (e.g., monthly reports).
    • Granularity: Group by minute, hour, day, week, month, or year.
    • Aggregations: Access value, increments, decrements, and difference per period.

Advanced Patterns

  1. Model-Backed Stats

    • Use StatsWriter for Eloquent models (e.g., tenant-specific stats):
      // Track orders per tenant
      StatsWriter::for($tenant->orders)->increase(1);
      
    • Query with:
      StatsQuery::for($tenant->orders)
          ->groupByMonth()
          ->get();
      
  2. Custom Attributes

    • Add metadata (e.g., payment type) to stats:
      StatsWriter::for('subscription', ['type' => 'premium'])->increase(1);
      
    • Query with:
      StatsQuery::for('subscription', ['type' => 'premium'])
          ->groupByWeek()
          ->get();
      
  3. Direct Writer Access

    • Use writer() for bulk operations:
      SubscriptionStats::writer()->set(100); // Override current value
      
  4. Integration with Analytics

    • Export stats to external services (e.g., Google Analytics) via:
      $stats = SubscriptionStats::query()->get();
      $analyticsService->send($stats);
      

Testing Strategies

  1. Unit Tests

    • Mock BaseStats methods:
      $stats = Mockery::mock(SubscriptionStats::class);
      $stats->shouldReceive('increase')->once();
      
  2. Database Tests

    • Assert stats in migrations/seeds:
      $this->assertDatabaseHas('stats', [
          'name' => 'subscription',
          'value' => 100,
      ]);
      
  3. Query Validation

    • Verify grouped results:
      $stats = SubscriptionStats::query()->groupByWeek()->get();
      $this->assertCount(4, $stats); // 4 weeks of data
      

Gotchas and Tips

Common Pitfalls

  1. Migration Conflicts

    • Issue: Running migrations after schema changes (e.g., adding columns).
    • Fix: Reset migrations or use --force:
      php artisan migrate:fresh --env=testing
      
  2. Timestamp Precision

    • Issue: groupByMinute() may return empty results for sparse data.
    • Fix: Use groupByHour() or pad data with set() calls.
  3. Relationship Queries

    • Issue: StatsQuery::for($model->relationship()) fails if the relationship is lazy-loaded.
    • Fix: Eager-load relationships:
      $tenant = Tenant::with('orders')->find(1);
      StatsQuery::for($tenant->orders)->get();
      
  4. Concurrency Issues

    • Issue: Race conditions when multiple processes call increase()/decrease().
    • Fix: Use database transactions:
      DB::transaction(function () {
          SubscriptionStats::increase();
      });
      

Debugging Tips

  1. Query Logging

    • Enable Laravel query logging to inspect SQL:
      \DB::enableQueryLog();
      SubscriptionStats::query()->get();
      \DB::getQueryLog(); // Inspect generated queries
      
  2. DataPoint Inspection

    • Dump raw DataPoint objects to debug:
      $stats = SubscriptionStats::query()->get();
      dd($stats->first()->toArray());
      
  3. Migration Rollback

    • Reset stats table for testing:
      php artisan migrate:fresh --env=testing
      

Extension Points

  1. Custom Storage

    • Override getTable() in BaseStats for non-default tables:
      public function getTable(): string
      {
          return 'custom_stats';
      }
      
  2. Event Hooks

    • Extend BaseStats to trigger events:
      protected static function afterIncrease()
      {
          event(new StatIncreased(self::class));
      }
      
  3. Period Customization

    • Add custom grouping (e.g., "quarter") by extending StatsQuery:
      public function groupByQuarter()
      {
          return $this->groupBy(fn () => 'quarter');
      }
      
  4. API Integration

    • Create a facade for external APIs:
      // app/Facades/StatsFacade.php
      public static function syncFromApi()
      {
          $count = ApiClient::getSubscriptionCount();
          SubscriptionStats::set($count);
      }
      

Performance Optimizations

  1. Batch Writes

    • Use set() for bulk updates instead of incremental calls:
      // Instead of:
      foreach ($subscriptions as $sub) {
          SubscriptionStats::increase();
      }
      // Do:
      SubscriptionStats::set(count($subscriptions));
      
  2. Query Caching

    • Cache frequent queries (e.g., dashboard stats):
      $stats = Cache::remember('subscription_stats_weekly', now()->addWeek(), function () {
          return SubscriptionStats::query()->groupByWeek()->get();
      });
      
  3. Indexing

    • Add indexes to stats table for large datasets:
      Schema::table('stats', function (Blueprint $table) {
          $table->index(['name', 'created_at']);
      });
      
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.
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope
anil/file-picker
broqit/fields-ai