option('dry-run'); $showDiff = (bool) $this->option('show-diff'); $ids = array_filter((array) $this->option('id')); $normalizer = new RichTextMarkdownNormalizer; $query = ProjectProposal::query()->orderBy('id'); if ($ids !== []) { $query->whereIn('id', $ids); } $total = (clone $query)->count(); if ($total === 0) { $this->warn('No project proposals to process.'); return self::SUCCESS; } $this->info(sprintf( '%s %d project proposal description(s)%s.', $dryRun ? 'Analyzing' : 'Normalizing', $total, $dryRun ? ' (dry-run)' : '', )); $changed = 0; $unchanged = 0; $failed = 0; $query->lazy()->each(function (ProjectProposal $proposal) use ($normalizer, $dryRun, $showDiff, &$changed, &$unchanged, &$failed): void { $original = (string) ($proposal->description ?? ''); try { $normalized = (string) ($normalizer->normalize($original) ?? ''); } catch (\Throwable $exception) { $failed++; $this->error(sprintf('#%d %s — normalization failed: %s', $proposal->id, $proposal->name, $exception->getMessage())); return; } if ($original === $normalized) { $unchanged++; return; } $changed++; $this->line(sprintf('~ #%d %s', $proposal->id, $proposal->name)); if ($showDiff) { $this->line(' - '.$this->preview($original).''); $this->line(' + '.$this->preview($normalized).''); } if (! $dryRun) { $proposal->description = $normalized; $proposal->saveQuietly(); } }); $this->newLine(); $this->info(sprintf( 'Done. Changed: %d, unchanged: %d, failed: %d%s', $changed, $unchanged, $failed, $dryRun ? ' (no writes performed)' : '', )); return $failed > 0 ? self::FAILURE : self::SUCCESS; } private function preview(string $value): string { $collapsed = preg_replace('/\s+/', ' ', trim($value)) ?? ''; return mb_strimwidth($collapsed, 0, 140, '…'); } }