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 stat changes over time. Define a stats class, call increase/decrease on events, then query totals and increments/decrements across date ranges grouped by day/week/month.

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:

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

    use App\Stats\SubscriptionStats;
    
    // When a user subscribes
    SubscriptionStats::increase();
    
    // When a user cancels
    SubscriptionStats::decrease();
    
  4. Query Stats:

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

Implementation Patterns

Core Workflow

  1. Define Stats Classes: Create dedicated classes for each metric (e.g., UserStats, OrderStats).

    class UserStats extends BaseStats {
        public function getName(): string {
            return 'user_activity';
        }
    }
    
  2. Integrate with Business Logic:

    • Events: Hook into created, deleted, or custom events.
      // In UserObserver.php
      public function created(User $user) {
          UserStats::increase();
      }
      
    • API/Webhooks: Update stats via external triggers.
      // Handle webhook for order completion
      OrderCompleted::increase();
      
  3. Querying Patterns:

    • Time-Based Aggregation:
      $dailyStats = OrderStats::query()
          ->start(now()->subDays(7))
          ->end(now())
          ->groupByDay()
          ->get();
      
    • Dynamic Periods:
      $stats = UserStats::query()
          ->start(request('start_date'))
          ->end(request('end_date'))
          ->groupByMonth()
          ->get();
      
  4. Custom Attributes: Track stats with additional context (e.g., by payment_method):

    // Write
    StatsWriter::for(OrderStats::class, ['payment_method' => 'credit_card'])->increase();
    
    // Query
    $stats = StatsQuery::for(OrderStats::class, ['payment_method' => 'credit_card'])
        ->groupByWeek()
        ->get();
    
  5. Relationship-Based Stats: Track stats for polymorphic relationships (e.g., TenantOrder):

    // Write
    StatsWriter::for($tenant->orders)->increase();
    
    // Query
    $stats = StatsQuery::for($tenant->orders)
        ->groupByMonth()
        ->get();
    

Advanced Patterns

  1. Bulk Updates: Use set() for initial loads or external syncs:

    $totalUsers = User::count();
    UserStats::set($totalUsers);
    
  2. Time-Shifted Updates: Record historical data:

    StatsWriter::for(OrderStats::class)->increase(1, now()->subDays(2));
    
  3. Caching Queries: Cache frequent queries (e.g., dashboard metrics):

    $stats = Cache::remember('user_stats_weekly', now()->addWeek(), function() {
        return UserStats::query()->groupByWeek()->get();
    });
    
  4. Real-Time Dashboards: Use Laravel Echo/Pusher to push stat updates:

    // Broadcast stat changes
    Echo.channel('stats')
        .listen('StatUpdated', (data) => {
            updateDashboard(data);
        });
    

Gotchas and Tips

Pitfalls

  1. Migration Conflicts:

    • If migrating from v1.x to v2.x, run:
      php artisan stats:migrate-fresh
      
    • Ensure no existing stats_events table conflicts with the new schema.
  2. Time Zone Handling:

    • Queries use the system timezone. Explicitly set timezone in queries if needed:
      StatsQuery::for(OrderStats::class)
          ->start(now('America/New_York')->subMonth())
          ->groupByDay();
      
  3. Performance with Large Datasets:

    • Avoid querying excessive time ranges (e.g., start(now()->subYears(10))).
    • Use groupByMonth() or groupByYear() for long-term trends.
  4. Concurrent Writes:

    • increase()/decrease() are atomic, but bulk operations may race. Use transactions for critical paths:
      DB::transaction(function() {
          StatsWriter::for(OrderStats::class)->increase(100);
          // Other DB operations...
      });
      
  5. Custom Attributes Quirks:

    • Attributes must be serializable (no closures or resources).
    • Use simple arrays or JSON-serializable data:
      StatsWriter::for(OrderStats::class, ['tags' => ['premium', 'recurring']])->increase();
      

Debugging Tips

  1. Query Inspection: Enable query logging to debug slow queries:

    DB::enableQueryLog();
    $stats = UserStats::query()->groupByWeek()->get();
    dd(DB::getQueryLog());
    
  2. DataPoint Validation: Check for malformed data in the stats_events table:

    SELECT * FROM stats_events WHERE type NOT IN ('change', 'set') LIMIT 10;
    
  3. Migration Rollback: If migrations fail, manually drop the table:

    DROP TABLE stats_events;
    

Extension Points

  1. Custom Storage: Override the default StatsRepository to use Redis or another store:

    // app/Providers/AppServiceProvider.php
    public function register() {
        $this->app->bind(
            \Spatie\Stats\StatsRepository::class,
            \App\Stats\RedisStatsRepository::class
        );
    }
    
  2. Event Listeners: Extend BaseStats to trigger events:

    class SubscriptionStats extends BaseStats {
        protected static function afterIncrease() {
            event(new StatIncreased('subscriptions'));
        }
    }
    
  3. API Resources: Format stats for APIs:

    // app/Http/Resources/StatResource.php
    public function toArray($request) {
        return [
            'period' => $this->start,
            'value' => $this->value,
            'trend' => $this->difference,
        ];
    }
    
  4. Testing: Use StatsTestCase for isolated tests:

    use Spatie\Stats\Testing\StatsTestCase;
    
    class SubscriptionStatsTest extends StatsTestCase {
        public function test_subscription_trend() {
            SubscriptionStats::increase();
            $stats = SubscriptionStats::query()->groupByDay()->get();
            $this->assertEquals(1, $stats[0]->value);
        }
    }
    

Configuration Quirks

  1. Table Prefix: Ensure the stats_events table uses the correct prefix if your app uses one:

    DB_TABLE_PREFIX=myapp_
    

    Then update the migration or use:

    StatsQuery::for(OrderStats::class)->table('myapp_stats_events');
    
  2. Laravel 10+: The package supports Laravel 10+ out of the box. For older versions, pin to v1.x:

    composer require spatie/laravel-stats:^1.0
    
  3. SQLite Support: Ensure your config/database.php uses the correct SQLite driver and path. The package includes SQLite-specific optimizations.

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.
codraw/framework-extra-bundle
codraw/messenger
codraw/security
codraw/mailer
codraw/contracts
codraw/profiling
codraw/dependency-injection
codraw/tester
codraw/core
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony