116 lines
3.5 KiB
PHP
116 lines
3.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Services\UserStatsService;
|
|
use Carbon\Carbon;
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Collection;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class SyncArtworkCreatedAtCommand extends Command
|
|
{
|
|
public function __construct(private readonly UserStatsService $userStats)
|
|
{
|
|
parent::__construct();
|
|
}
|
|
|
|
protected $signature = 'artworks:sync-created-at
|
|
{--chunk=500 : Number of artworks to process per batch}
|
|
{--only-null : Update only artworks whose created_at is null}
|
|
{--dry-run : Preview changes without writing updates}';
|
|
|
|
protected $description = 'Copy artworks.published_at into artworks.created_at for published artworks.';
|
|
|
|
public function handle(): int
|
|
{
|
|
$chunk = max(1, (int) $this->option('chunk'));
|
|
$onlyNull = (bool) $this->option('only-null');
|
|
$dryRun = (bool) $this->option('dry-run');
|
|
|
|
if ($dryRun) {
|
|
$this->warn('[DRY RUN] No changes will be written.');
|
|
}
|
|
|
|
$query = DB::table('artworks')
|
|
->select(['id', 'user_id', 'created_at', 'published_at'])
|
|
->whereNotNull('published_at')
|
|
->orderBy('id');
|
|
|
|
if ($onlyNull) {
|
|
$query->whereNull('created_at');
|
|
}
|
|
|
|
$processed = 0;
|
|
$updated = 0;
|
|
$unchanged = 0;
|
|
|
|
$affectedUserIds = [];
|
|
|
|
$query->chunkById($chunk, function (Collection $rows) use (&$processed, &$updated, &$unchanged, &$affectedUserIds, $dryRun): void {
|
|
foreach ($rows as $row) {
|
|
$processed++;
|
|
|
|
$publishedAt = $this->normalizeTimestamp($row->published_at ?? null);
|
|
$createdAt = $this->normalizeTimestamp($row->created_at ?? null);
|
|
|
|
if ($publishedAt === null) {
|
|
$unchanged++;
|
|
continue;
|
|
}
|
|
|
|
if ($createdAt === $publishedAt) {
|
|
$unchanged++;
|
|
continue;
|
|
}
|
|
|
|
if ($dryRun) {
|
|
$this->line(sprintf(
|
|
'[dry] Would update artwork id=%d created_at %s => %s',
|
|
(int) $row->id,
|
|
$createdAt ?? '<null>',
|
|
$publishedAt
|
|
));
|
|
$updated++;
|
|
continue;
|
|
}
|
|
|
|
DB::table('artworks')
|
|
->where('id', (int) $row->id)
|
|
->update([
|
|
'created_at' => $publishedAt,
|
|
'updated_at' => now()->toDateTimeString(),
|
|
]);
|
|
|
|
$affectedUserIds[(int) $row->user_id] = true;
|
|
$updated++;
|
|
$this->line(sprintf('[update] artwork id=%d created_at => %s', (int) $row->id, $publishedAt));
|
|
}
|
|
}, 'id');
|
|
|
|
if (! $dryRun) {
|
|
foreach (array_keys($affectedUserIds) as $userId) {
|
|
$this->userStats->recomputeUser((int) $userId);
|
|
}
|
|
}
|
|
|
|
$this->info(sprintf('Finished. processed=%d updated=%d unchanged=%d', $processed, $updated, $unchanged));
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
private function normalizeTimestamp(mixed $value): ?string
|
|
{
|
|
if ($value === null || $value === '') {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
return Carbon::parse((string) $value)->toDateTimeString();
|
|
} catch (\Throwable) {
|
|
return null;
|
|
}
|
|
}
|
|
} |