$importSignature,
'offset' => 0,
'statement' => '',
'in_quote' => false,
'escaped' => false,
'executed' => 0,
'done' => false,
'started_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
];
}
function save_old_site_import_progress(string $path, array $progress): void
{
$progress['updated_at'] = date('Y-m-d H:i:s');
file_put_contents($path, json_encode($progress, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
}
function execute_old_site_import_batch(string $sqlPath, string $progressPath, array $progress): array
{
if (!is_file($sqlPath)) {
throw new RuntimeException('Файл импорта не найден.');
}
@set_time_limit(20);
$pdo = Database::pdo();
$handle = fopen($sqlPath, 'rb');
if ($handle === false) {
throw new RuntimeException('Не удалось открыть файл импорта.');
}
$fileSize = max(1, filesize($sqlPath) ?: 1);
$offset = max(0, (int) ($progress['offset'] ?? 0));
$statement = (string) ($progress['statement'] ?? '');
$executedTotal = (int) ($progress['executed'] ?? 0);
$executedBatch = 0;
$inQuote = (bool) ($progress['in_quote'] ?? false);
$escaped = (bool) ($progress['escaped'] ?? false);
$started = microtime(true);
$maxSeconds = 7.0;
$maxStatements = 500;
$pdo->exec('SET FOREIGN_KEY_CHECKS = 0');
fseek($handle, $offset);
while (($chunk = fread($handle, 256 * 1024)) !== false && $chunk !== '') {
$length = strlen($chunk);
$chunkStartOffset = $offset;
for ($i = 0; $i < $length; $i++) {
$char = $chunk[$i];
$statement .= $char;
$offset = $chunkStartOffset + $i + 1;
if ($inQuote) {
if ($escaped) {
$escaped = false;
} elseif ($char === '\\') {
$escaped = true;
} elseif ($char === "'") {
$inQuote = false;
}
continue;
}
if ($char === "'") {
$inQuote = true;
continue;
}
if ($char !== ';') {
continue;
}
$sql = trim($statement);
$statement = '';
if ($sql === '') {
continue;
}
$pdo->exec($sql);
$executedBatch++;
$executedTotal++;
if ($executedBatch >= $maxStatements || (microtime(true) - $started) >= $maxSeconds) {
$progress = [
'offset' => $offset,
'statement' => '',
'in_quote' => false,
'escaped' => false,
'executed' => $executedTotal,
'done' => false,
'import_signature' => $progress['import_signature'] ?? '',
'started_at' => $progress['started_at'] ?? date('Y-m-d H:i:s'),
];
save_old_site_import_progress($progressPath, $progress);
$pdo->exec('SET FOREIGN_KEY_CHECKS = 1');
fclose($handle);
return $progress + [
'batch_executed' => $executedBatch,
'percent' => min(99.9, round(($offset / $fileSize) * 100, 1)),
];
}
}
}
fclose($handle);
$tail = trim($statement);
if ($tail !== '') {
$pdo->exec($tail);
$executedBatch++;
$executedTotal++;
}
$pdo->exec('SET FOREIGN_KEY_CHECKS = 1');
$progress = [
'offset' => $fileSize,
'statement' => '',
'in_quote' => false,
'escaped' => false,
'executed' => $executedTotal,
'done' => true,
'import_signature' => $progress['import_signature'] ?? '',
'started_at' => $progress['started_at'] ?? date('Y-m-d H:i:s'),
];
save_old_site_import_progress($progressPath, $progress);
return $progress + [
'batch_executed' => $executedBatch,
'percent' => 100,
];
}
function normalize_old_site_product_categories(PDO $pdo): void
{
$pdo->exec(
"UPDATE products p
SET p.category_id = (
SELECT pcl.category_id
FROM product_category_links pcl
INNER JOIN categories c ON c.id = pcl.category_id
WHERE pcl.product_id = p.id
AND c.is_active = 1
ORDER BY c.parent_id IS NULL, pcl.sort_order, pcl.category_id
LIMIT 1
)
WHERE p.legacy_path LIKE '/katalog/product/view/1/%'
AND EXISTS (
SELECT 1
FROM product_category_links pcl_exists
WHERE pcl_exists.product_id = p.id
)"
);
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
try {
$action = (string) ($_POST['action'] ?? 'start');
if ($action === 'start') {
$progress = old_site_import_default_progress($importSignature);
save_old_site_import_progress($progressPath, $progress);
}
if (($progress['import_signature'] ?? null) !== $importSignature) {
throw new RuntimeException('Файл импорта обновился. Нажмите «Запустить импорт заново».');
}
$progress = execute_old_site_import_batch($sqlPath, $progressPath, $progress);
if (!empty($progress['done'])) {
normalize_old_site_product_categories(Database::pdo());
$message = 'Импорт выполнен. SQL-команд выполнено: ' . (int) $progress['executed'] . '.';
} else {
$message = 'Импорт продолжается: ' . (float) $progress['percent'] . '%, команд за этот шаг: ' . (int) $progress['batch_executed'] . '.';
}
} catch (Throwable $exception) {
$error = $exception->getMessage();
}
}
$fileSize = is_file($sqlPath) ? max(1, filesize($sqlPath) ?: 1) : 1;
$progressPercent = !empty($progress['done'])
? 100
: min(99.9, round((((int) ($progress['offset'] ?? 0)) / $fileSize) * 100, 1));
?>
Импорт старого сайта - Рыбсток
Админка
Импорт старого сайта
Страница переносит подготовленные данные Joomla/JoomShopping в новую структуру Рыбсток.
Импорт выполняется маленькими шагами, чтобы сервер не обрывал процесс по 504 Gateway Timeout.
= e($message) ?>
= e($error) ?>
- Категорий
- = e((string) ($summary['categories'] ?? 0)) ?>
- Товаров
- = e((string) ($summary['products'] ?? 0)) ?>
- Фасовок
- = e((string) ($summary['product_variants'] ?? 0)) ?>
- Связей товар-категория
- = e((string) ($summary['product_category_links'] ?? 0)) ?>
- Картинок товаров
- = e((string) ($summary['copied_product_images'] ?? 0)) ?>
- Картинок категорий
- = e((string) ($summary['copied_category_images'] ?? 0)) ?>
- Картинок из описаний
- = e((string) ($summary['copied_content_images'] ?? 0)) ?>
- Редиректов категорий из sitemap
- = e((string) ($summary['sitemap_category_redirects'] ?? 0)) ?>
- SQL-файл
- = e((string) ($summary['sql_file'] ?? '')) ?>
= e((string) $progressPercent) ?>%
Выполнено SQL-команд: = e((string) ((int) ($progress['executed'] ?? 0))) ?>
Вернуться в админку