option('days') ?? config('press_releases.events.retention_days')); if ($days < 1) { $this->error('Aufbewahrungszeitraum muss mindestens einen Tag betragen.'); return self::FAILURE; } $watermark = AppSetting::getValue(AggregatePressReleaseViews::WatermarkKey); if ($watermark === null) { $this->error( 'Wasserstandsmarke des Aufruf-Zählers fehlt — es ist unbekannt, welche Ereignisse ' .'bereits in `hits` verbucht sind. Erst "php artisan press-releases:aggregate-views" laufen lassen.' ); return self::FAILURE; } $watermark = (int) $watermark; $cutoff = now()->subDays($days); $chunk = max(100, (int) $this->option('chunk')); $expired = PressReleaseEvent::query() ->where('id', '<=', $watermark) ->where('created_at', '<', $cutoff); if ($this->option('dry-run')) { $this->info(sprintf( '[Dry-Run] %d Ereignisse älter als %s und bereits verbucht (Stand Event-ID %d).', (clone $expired)->count(), $cutoff->format('d.m.Y'), $watermark, )); return self::SUCCESS; } $deleted = 0; while (true) { $removed = (clone $expired)->limit($chunk)->delete(); if ($removed === 0) { break; } $deleted += $removed; } $this->info(sprintf( 'Ereignis-Log bereinigt: %d Einträge vor dem %s gelöscht (Aufbewahrung %d Tage).', $deleted, $cutoff->format('d.m.Y'), $days, )); return self::SUCCESS; } }