Add news article comments and reactions
This commit is contained in:
186
app/Console/Commands/ExportLegacyNewsCommentsSqlCommand.php
Normal file
186
app/Console/Commands/ExportLegacyNewsCommentsSqlCommand.php
Normal file
@@ -0,0 +1,186 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ExportLegacyNewsCommentsSqlCommand extends Command
|
||||
{
|
||||
protected $signature = 'news:comments-export-legacy-sql
|
||||
{--path=database/sql/news_article_comments_legacy_import.sql : Output SQL file path}
|
||||
{--skip-empty : Skip comments with empty or whitespace-only content}
|
||||
{--table= : Override legacy source table name (defaults to auto-detect news_comment/news_comments)}';
|
||||
|
||||
protected $description = 'Generate a production-safe SQL file for legacy news comments import';
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
try {
|
||||
DB::connection('legacy')->getPdo();
|
||||
} catch (\Throwable $exception) {
|
||||
$this->error('Cannot connect to legacy database: ' . $exception->getMessage());
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$legacyTable = $this->resolveLegacyTable();
|
||||
if ($legacyTable === null) {
|
||||
$this->error('Legacy table `news_comment` or `news_comments` was not found.');
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$outputPath = $this->resolveOutputPath((string) $this->option('path'));
|
||||
$skipEmpty = (bool) $this->option('skip-empty');
|
||||
|
||||
$directory = dirname($outputPath);
|
||||
if (! is_dir($directory)) {
|
||||
mkdir($directory, 0777, true);
|
||||
}
|
||||
|
||||
$handle = fopen($outputPath, 'wb');
|
||||
if ($handle === false) {
|
||||
$this->error('Unable to write SQL file: ' . $outputPath);
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$written = 0;
|
||||
$skippedEmpty = 0;
|
||||
$legacyNewsIds = [];
|
||||
|
||||
fwrite($handle, "-- Legacy news comments import generated at " . now()->toDateTimeString() . PHP_EOL);
|
||||
fwrite($handle, "START TRANSACTION;" . PHP_EOL . PHP_EOL);
|
||||
|
||||
DB::connection('legacy')
|
||||
->table($legacyTable)
|
||||
->orderBy('comment_id')
|
||||
->chunk(500, function ($rows) use ($handle, $skipEmpty, &$written, &$skippedEmpty, &$legacyNewsIds): void {
|
||||
foreach ($rows as $row) {
|
||||
$legacyId = (int) ($row->comment_id ?? 0);
|
||||
$legacyNewsId = (int) ($row->news_id ?? 0);
|
||||
$legacyUserId = (int) ($row->user_id ?? 0);
|
||||
$body = trim((string) ($row->message ?? ''));
|
||||
|
||||
if ($legacyId < 1 || $legacyNewsId < 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($body === '') {
|
||||
if ($skipEmpty) {
|
||||
$skippedEmpty++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$body = '[no content]';
|
||||
}
|
||||
|
||||
$legacyNewsIds[$legacyNewsId] = $legacyNewsId;
|
||||
|
||||
$authorName = trim((string) ($row->author ?? ''));
|
||||
$timestamp = $this->normalizeTimestamp($row->posted ?? null);
|
||||
$renderedBody = nl2br(e($body));
|
||||
$userExpression = $legacyUserId > 0
|
||||
? "CASE WHEN EXISTS (SELECT 1 FROM users WHERE users.id = {$legacyUserId} AND users.deleted_at IS NULL) THEN {$legacyUserId} ELSE NULL END"
|
||||
: 'NULL';
|
||||
|
||||
$statement = "INSERT IGNORE INTO news_article_comments (legacy_id, legacy_user_id, article_id, user_id, parent_id, author_name, body, rendered_body, status, legacy_posted_at, created_at, updated_at, deleted_at)\n"
|
||||
. "SELECT {$legacyId}, " . ($legacyUserId > 0 ? (string) $legacyUserId : 'NULL') . ", news_articles.id, {$userExpression}, NULL, " . $this->quote($authorName !== '' ? $authorName : null) . ", " . $this->quote($body) . ", " . $this->quote($renderedBody) . ", 'visible', " . $this->quote($timestamp) . ", " . $this->quote($timestamp) . ", " . $this->quote($timestamp) . ", NULL\n"
|
||||
. "FROM news_articles\n"
|
||||
. "WHERE news_articles.legacy_news_id = {$legacyNewsId}\n"
|
||||
. "LIMIT 1;\n\n";
|
||||
|
||||
fwrite($handle, $statement);
|
||||
$written++;
|
||||
}
|
||||
});
|
||||
|
||||
if ($legacyNewsIds !== []) {
|
||||
foreach (array_chunk(array_values($legacyNewsIds), 250) as $chunk) {
|
||||
fwrite($handle, 'UPDATE news_articles SET comments_enabled = 1 WHERE legacy_news_id IN (' . implode(', ', array_map('intval', $chunk)) . ');' . PHP_EOL);
|
||||
}
|
||||
|
||||
fwrite($handle, PHP_EOL);
|
||||
}
|
||||
|
||||
fwrite($handle, 'COMMIT;' . PHP_EOL);
|
||||
fclose($handle);
|
||||
|
||||
$this->info('SQL export written to ' . $outputPath);
|
||||
$this->table(
|
||||
['Result', 'Count'],
|
||||
[
|
||||
['Statements written', $written],
|
||||
['Skipped - empty body', $skippedEmpty],
|
||||
['Articles enabled for comments', count($legacyNewsIds)],
|
||||
]
|
||||
);
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
private function resolveLegacyTable(): ?string
|
||||
{
|
||||
$configured = trim((string) $this->option('table'));
|
||||
if ($configured !== '') {
|
||||
return DB::connection('legacy')->getSchemaBuilder()->hasTable($configured) ? $configured : null;
|
||||
}
|
||||
|
||||
foreach (['news_comment', 'news_comments'] as $candidate) {
|
||||
if (DB::connection('legacy')->getSchemaBuilder()->hasTable($candidate)) {
|
||||
return $candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function resolveOutputPath(string $path): string
|
||||
{
|
||||
$trimmed = trim($path);
|
||||
|
||||
if ($trimmed === '') {
|
||||
return base_path('database/sql/news_article_comments_legacy_import.sql');
|
||||
}
|
||||
|
||||
if (preg_match('/^[A-Za-z]:\\\\|^\\\\\\\\|^\//', $trimmed) === 1) {
|
||||
return $trimmed;
|
||||
}
|
||||
|
||||
return base_path(str_replace(['/', '\\\\'], DIRECTORY_SEPARATOR, $trimmed));
|
||||
}
|
||||
|
||||
private function normalizeTimestamp(mixed $value): string
|
||||
{
|
||||
$raw = trim((string) ($value ?? ''));
|
||||
|
||||
if ($raw === '' || str_starts_with($raw, '0000-00-00')) {
|
||||
return now()->toDateTimeString();
|
||||
}
|
||||
|
||||
try {
|
||||
return Carbon::parse($raw)->toDateTimeString();
|
||||
} catch (\Throwable) {
|
||||
return now()->toDateTimeString();
|
||||
}
|
||||
}
|
||||
|
||||
private function quote(?string $value): string
|
||||
{
|
||||
if ($value === null) {
|
||||
return 'NULL';
|
||||
}
|
||||
|
||||
$escaped = str_replace(
|
||||
["\\", "\0", "\n", "\r", "\x1a", "'"],
|
||||
["\\\\", "\\0", "\\n", "\\r", "\\Z", "\\'"],
|
||||
$value,
|
||||
);
|
||||
|
||||
return "'{$escaped}'";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user