messages implemented
This commit is contained in:
143
app/Console/Commands/MigrateSmileys.php
Normal file
143
app/Console/Commands/MigrateSmileys.php
Normal file
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\LegacySmileyMapper;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* php artisan skinbase:migrate-smileys
|
||||
*
|
||||
* Scans artworks.description, artwork_comments.content, and forum_posts.content,
|
||||
* replaces legacy smiley codes (:beer, :lol, etc.) with Unicode emoji.
|
||||
*
|
||||
* Options:
|
||||
* --dry-run Show what would change without writing to DB
|
||||
* --chunk=200 Rows processed per batch (default 200)
|
||||
* --table=artworks Limit scan to one table
|
||||
*/
|
||||
class MigrateSmileys extends Command
|
||||
{
|
||||
protected $signature = 'skinbase:migrate-smileys
|
||||
{--dry-run : Preview changes without writing to the database}
|
||||
{--chunk=200 : Number of rows to process per batch}
|
||||
{--table= : Limit scan to a single table (artworks|artwork_comments|forum_posts)}';
|
||||
|
||||
protected $description = 'Convert legacy :smiley: codes to Unicode emoji in content fields.';
|
||||
|
||||
/** Tables and their content columns to scan. */
|
||||
private const TARGETS = [
|
||||
'artworks' => 'description',
|
||||
'artwork_comments' => 'content',
|
||||
'forum_posts' => 'content',
|
||||
];
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$dryRun = (bool) $this->option('dry-run');
|
||||
$chunk = max(1, (int) $this->option('chunk'));
|
||||
$tableOpt = $this->option('table');
|
||||
|
||||
$targets = self::TARGETS;
|
||||
if ($tableOpt) {
|
||||
if (! isset($targets[$tableOpt])) {
|
||||
$this->error("Unknown table: {$tableOpt}. Allowed: " . implode(', ', array_keys($targets)));
|
||||
return self::FAILURE;
|
||||
}
|
||||
$targets = [$tableOpt => $targets[$tableOpt]];
|
||||
}
|
||||
|
||||
if ($dryRun) {
|
||||
$this->warn('DRY-RUN mode — no changes will be written.');
|
||||
}
|
||||
|
||||
$totalChanged = 0;
|
||||
$totalRows = 0;
|
||||
|
||||
foreach ($targets as $table => $column) {
|
||||
$this->line("Scanning <info>{$table}.{$column}</info>…");
|
||||
|
||||
[$changed, $rows] = $this->processTable($table, $column, $chunk, $dryRun);
|
||||
|
||||
$totalChanged += $changed;
|
||||
$totalRows += $rows;
|
||||
|
||||
$this->line(" → {$rows} rows scanned, {$changed} updated.");
|
||||
}
|
||||
|
||||
$this->newLine();
|
||||
$this->info("Summary: {$totalRows} rows scanned, {$totalChanged} rows " . ($dryRun ? 'would be ' : '') . 'updated.');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
private function processTable(
|
||||
string $table,
|
||||
string $column,
|
||||
int $chunk,
|
||||
bool $dryRun
|
||||
): array {
|
||||
$totalChanged = 0;
|
||||
$totalRows = 0;
|
||||
|
||||
DB::table($table)
|
||||
->whereNotNull($column)
|
||||
->orderBy('id')
|
||||
->chunk($chunk, function ($rows) use ($table, $column, $dryRun, &$totalChanged, &$totalRows) {
|
||||
foreach ($rows as $row) {
|
||||
$original = $row->$column ?? '';
|
||||
$converted = LegacySmileyMapper::convert($original);
|
||||
|
||||
// Collapse emoji flood runs BEFORE size/DB checks so that
|
||||
// rows like ":beer :beer :beer …" (×500) don't exceed MEDIUMTEXT.
|
||||
$collapsed = LegacySmileyMapper::collapseFlood($converted);
|
||||
if ($collapsed !== $converted) {
|
||||
$beforeBytes = mb_strlen($converted, '8bit');
|
||||
$afterBytes = mb_strlen($collapsed, '8bit');
|
||||
$floodMsg = "[{$table}#{$row->id}] Emoji flood collapsed "
|
||||
. "({$beforeBytes} bytes \u{2192} {$afterBytes} bytes).";
|
||||
$this->warn(" {$floodMsg}");
|
||||
Log::warning($floodMsg);
|
||||
$converted = $collapsed;
|
||||
}
|
||||
|
||||
$totalRows++;
|
||||
|
||||
if ($converted === $original) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$totalChanged++;
|
||||
|
||||
$codes = LegacySmileyMapper::detect($original);
|
||||
$msg = "[{$table}#{$row->id}] Converting: " . implode(', ', $codes);
|
||||
$this->line(" {$msg}");
|
||||
Log::info($msg);
|
||||
|
||||
if (! $dryRun) {
|
||||
// Guard: MEDIUMTEXT max is 16,777,215 bytes.
|
||||
if (mb_strlen($converted, '8bit') > 16_777_215) {
|
||||
$warn = "[{$table}#{$row->id}] SKIP — converted content exceeds MEDIUMTEXT limit (" . mb_strlen($converted, '8bit') . " bytes). Row left unchanged.";
|
||||
$this->warn(" {$warn}");
|
||||
Log::warning($warn);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
DB::table($table)
|
||||
->where('id', $row->id)
|
||||
->update([$column => $converted]);
|
||||
} catch (\Throwable $e) {
|
||||
$err = "[{$table}#{$row->id}] DB error: {$e->getMessage()}";
|
||||
$this->warn(" {$err}");
|
||||
Log::error($err);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return [$totalChanged, $totalRows];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user