120 lines
4.5 KiB
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)');
|
|
}
|
|
}
|
|
} |