option('id')) ->map(static fn (mixed $id): int => (int) $id) ->filter(static fn (int $id): bool => $id > 0) ->values() ->all(); $force = (bool) $this->option('force'); $query = NewsArticle::query() ->select(['id', 'title', 'cover_image']) ->whereNotNull('cover_image') ->where('cover_image', '!=', ''); if ($ids !== []) { $query->whereIn('id', $ids); } $generated = 0; $skipped = 0; $failed = 0; $purged = 0; $query->orderBy('id')->chunkById(100, function ($articles) use (&$generated, &$skipped, &$failed, &$purged, $force): void { foreach ($articles as $article) { $path = trim((string) $article->cover_image); if (! NewsCoverImage::isManagedPath($path)) { $skipped++; continue; } try { $result = $this->covers->ensureVariants($path, $force); } catch (\Throwable $e) { $failed++; $this->warn(sprintf('Article %d failed: %s', (int) $article->id, $e->getMessage())); continue; } if (($result['generated'] ?? 0) > 0) { $generated++; if ($force && $this->purgeVariantCache($path, (int) $article->id)) { $purged++; } continue; } $skipped++; } }); $this->info(sprintf('News cover thumbnail generation complete: generated=%d skipped=%d failed=%d purged=%d', $generated, $skipped, $failed, $purged)); return $failed > 0 ? self::FAILURE : self::SUCCESS; } private function purgeVariantCache(string $path, int $articleId): bool { $variantPaths = array_values(array_map( static fn (string $variant): string => NewsCoverImage::variantPath($path, $variant), array_keys(NewsCoverImage::VARIANTS), )); return $this->cdnPurge->purgeArtworkObjectPaths($variantPaths, [ 'article_id' => $articleId, 'reason' => 'news_cover_thumbnails_regenerated', ]); } }