option('id') !== null ? max(1, (int) $this->option('id')) : null; $limit = $this->option('limit') !== null ? max(1, (int) $this->option('limit')) : null; $force = (bool) $this->option('force'); $dryRun = (bool) $this->option('dry-run'); $query = Artwork::query() ->whereNotNull('hash') ->where('hash', '!=', '') ->orderBy('id'); if ($artworkId !== null) { $query->whereKey($artworkId); } $processed = 0; $generated = 0; $planned = 0; $skipped = 0; $failed = 0; $query->chunkById(100, function ($artworks) use ($backfill, $force, $dryRun, $limit, &$processed, &$generated, &$planned, &$skipped, &$failed) { foreach ($artworks as $artwork) { if ($limit !== null && $processed >= $limit) { return false; } try { $result = $backfill->ensureSquareThumbnail($artwork, $force, $dryRun); $status = (string) ($result['status'] ?? 'skipped'); if ($status === 'generated') { $generated++; } elseif ($status === 'dry_run') { $planned++; } else { $skipped++; } } catch (Throwable $e) { $failed++; $this->warn(sprintf('Artwork %d failed: %s', (int) $artwork->getKey(), $e->getMessage())); } $processed++; } return true; }); $this->info(sprintf( 'Square thumbnail backfill complete. processed=%d generated=%d planned=%d skipped=%d failed=%d', $processed, $generated, $planned, $skipped, $failed, )); return $failed > 0 ? self::FAILURE : self::SUCCESS; } }