Files
SkinbaseNova/app/Console/Commands/NormalizeArtworkSlugsCommand.php

120 lines
4.5 KiB
PHP

<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
final class NormalizeArtworkSlugsCommand extends Command
{
protected $signature = 'artworks:normalize-slugs
{--dry-run : Show the slug changes without writing them}
{--chunk=500 : Number of artworks to process per chunk}
{--only-mismatched : Only update rows whose current slug differs from the normalized title slug}';
protected $description = 'Normalize existing artwork slugs from artwork titles without enforcing uniqueness.';
public function handle(): int
{
$dryRun = (bool) $this->option('dry-run');
$chunkSize = max(1, (int) $this->option('chunk'));
$onlyMismatched = (bool) $this->option('only-mismatched');
if (! $dryRun) {
$this->ensureSlugIsNotUnique();
}
$processed = 0;
$updated = 0;
DB::table('artworks')
->select(['id', 'title', 'slug'])
->orderBy('id')
->chunkById($chunkSize, function ($artworks) use ($dryRun, $onlyMismatched, &$processed, &$updated): void {
foreach ($artworks as $artwork) {
$processed++;
$normalizedSlug = Str::limit(Str::slug((string) ($artwork->title ?? '')) ?: 'artwork', 160, '');
$currentSlug = (string) ($artwork->slug ?? '');
if ($onlyMismatched && $currentSlug === $normalizedSlug) {
continue;
}
if ($currentSlug === $normalizedSlug) {
continue;
}
if ($dryRun) {
$this->line(sprintf('#%d %s => %s', $artwork->id, $currentSlug !== '' ? $currentSlug : '[empty]', $normalizedSlug));
$updated++;
continue;
}
DB::table('artworks')
->where('id', $artwork->id)
->update(['slug' => $normalizedSlug]);
$updated++;
}
});
if ($dryRun) {
$this->info(sprintf('Dry run complete. Checked %d artworks, %d would be updated.', $processed, $updated));
return self::SUCCESS;
}
$this->info(sprintf('Normalization complete. Checked %d artworks, updated %d.', $processed, $updated));
return self::SUCCESS;
}
private function ensureSlugIsNotUnique(): void
{
$driver = DB::getDriverName();
if ($driver === 'mysql') {
$indexes = collect(DB::select("SHOW INDEX FROM artworks WHERE Column_name = 'slug'"));
$indexes
->filter(fn ($index) => (int) ($index->Non_unique ?? 1) === 0)
->pluck('Key_name')
->filter()
->unique()
->each(function ($indexName): void {
$this->warn(sprintf('Dropping unique slug index %s before normalization.', $indexName));
DB::statement(sprintf('ALTER TABLE artworks DROP INDEX `%s`', str_replace('`', '``', (string) $indexName)));
});
$hasNonUniqueSlugIndex = $indexes->contains(fn ($index) => (string) ($index->Key_name ?? '') === 'artworks_slug_index' || (int) ($index->Non_unique ?? 0) === 1);
if (! $hasNonUniqueSlugIndex) {
DB::statement('CREATE INDEX artworks_slug_index ON artworks (slug)');
}
return;
}
if ($driver === 'sqlite') {
$indexes = collect(DB::select("PRAGMA index_list('artworks')"));
$indexes
->filter(function ($index): bool {
if ((int) ($index->unique ?? 0) !== 1) {
return false;
}
$columns = collect(DB::select(sprintf("PRAGMA index_info('%s')", str_replace("'", "''", (string) $index->name))))
->pluck('name')
->map(fn ($name) => (string) $name);
return $columns->contains('slug');
})
->pluck('name')
->each(fn ($indexName) => DB::statement(sprintf('DROP INDEX IF EXISTS "%s"', str_replace('"', '""', (string) $indexName))));
DB::statement('CREATE INDEX IF NOT EXISTS artworks_slug_index ON artworks (slug)');
}
}
}