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}'"; } }