option('period'); if (! in_array($period, ['24h', '7d'], true)) { $this->error("Invalid period '{$period}'. Use 24h or 7d."); return self::FAILURE; } [$viewsCol, $downloadsCol, $cutoff] = match ($period) { '24h' => ['views_24h', 'downloads_24h', now()->subDay()], default => ['views_7d', 'downloads_7d', now()->subDays(7)], }; $start = microtime(true); // ── 1. Zero the views window column ────────────────────────────────── // We have no per-view event log, so we reset the accumulator. $viewsReset = DB::table('artwork_stats')->update([$viewsCol => 0]); // ── 2. Recompute downloads window from the event log ───────────────── // artwork_downloads has created_at, so each row's window is accurate. // Chunked PHP loop avoids MySQL-only functions (GREATEST, INTERVAL) // so this command works in both MySQL (production) and SQLite (tests). $downloadsRecomputed = 0; DB::table('artwork_stats') ->orderBy('artwork_id') ->chunk(1000, function ($rows) use ($downloadsCol, $cutoff, &$downloadsRecomputed): void { foreach ($rows as $row) { $count = DB::table('artwork_downloads') ->where('artwork_id', $row->artwork_id) ->where('created_at', '>=', $cutoff) ->count(); DB::table('artwork_stats') ->where('artwork_id', $row->artwork_id) ->update([$downloadsCol => max(0, $count)]); $downloadsRecomputed++; } }); $elapsed = round(microtime(true) - $start, 2); $this->info("Period: {$period}"); $this->info(" {$viewsCol}: zeroed {$viewsReset} rows"); $this->info(" {$downloadsCol}: recomputed {$downloadsRecomputed} rows ({$elapsed}s)"); Log::info('ResetWindowedStats complete', [ 'period' => $period, 'views_col' => $viewsCol, 'views_rows_reset' => $viewsReset, 'downloads_col' => $downloadsCol, 'downloads_recomputed' => $downloadsRecomputed, 'elapsed_s' => $elapsed, ]); return self::SUCCESS; } }