option('days'); $isDryRun = $this->option('dry-run'); $cutoff = now()->subDays($days); $drafts = PressRelease::withoutGlobalScopes() ->where('status', PressReleaseStatus::Draft->value) ->where('updated_at', '<', $cutoff); // Abgelehnt und nie veröffentlicht: nur Meldungen, an denen noch // Bilder hängen — sonst wäre der Lauf jede Woche derselbe Zähler. $rejected = PressRelease::withoutGlobalScopes() ->where('status', PressReleaseStatus::Rejected->value) ->neverPublished() ->whereNull('legacy_id') ->where('updated_at', '<', $cutoff) ->whereHas('images'); $draftCount = $drafts->count(); $rejectedCount = $rejected->count(); if ($isDryRun) { $this->warn("[DRY-RUN] {$draftCount} Entwürfe würden archiviert, {$rejectedCount} abgelehnte Einreichungen würden bereinigt. Kein tatsächlicher Vorgang."); return self::SUCCESS; } // Bilder nie veröffentlichter Entwürfe werden mit dem Purge sofort // und vollständig gelöscht (Bildkennzeichnung E.3, Decision-Update E4). $remover = app(PressReleaseImageRemover::class); $failedPressReleaseIds = []; $imagesRemoved = $this->removeImages((clone $drafts)->neverPublished(), $remover, $failedPressReleaseIds) + $this->removeImages(clone $rejected, $remover, $failedPressReleaseIds); // Entwürfe mit gescheiterter Bildlöschung behalten ihren Status, // sonst fielen sie aus dem nächsten Lauf heraus. $archived = $drafts->when($failedPressReleaseIds !== [], fn (Builder $query) => $query->whereKeyNot($failedPressReleaseIds)) ->update(['status' => PressReleaseStatus::Archived->value]); $failed = count($failedPressReleaseIds); $this->info("PM-Entwürfe archiviert: {$archived} Einträge, abgelehnte Einreichungen bereinigt: {$rejectedCount}, Bilder gelöscht: {$imagesRemoved}, Meldungen mit fehlgeschlagener Bildlöschung: {$failed}."); if ($failed > 0) { $this->error("Bildlöschung fehlgeschlagen: {$failed} Meldung(en) — Details im Log, sie werden beim nächsten Lauf wiederholt."); return self::FAILURE; } return self::SUCCESS; } /** * @param Builder $query * @param list $failedPressReleaseIds */ private function removeImages(Builder $query, PressReleaseImageRemover $remover, array &$failedPressReleaseIds): int { $removed = 0; $query->with('images')->chunkById(200, function ($pressReleases) use ($remover, &$removed, &$failedPressReleaseIds): void { foreach ($pressReleases as $pressRelease) { foreach ($pressRelease->images as $image) { try { $remover->remove($image); $removed++; } catch (Throwable $exception) { $failedPressReleaseIds[] = (int) $pressRelease->getKey(); Log::error('purge-drafts: Bild konnte nicht gelöscht werden, wird beim nächsten Lauf wiederholt.', [ 'press_release_id' => $pressRelease->getKey(), 'press_release_image_id' => $image->getKey(), 'error' => $exception->getMessage(), ]); } } } }); $failedPressReleaseIds = array_values(array_unique($failedPressReleaseIds)); return $removed; } }