From de5266f98f15b6d9d63cdcd2f474a970d3ab35ec Mon Sep 17 00:00:00 2001 From: Michael Chus Date: Wed, 21 Jan 2026 04:08:13 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BA=D1=80=D0=B8=D1=82=D0=B8=D1=87?= =?UTF-8?q?=D0=B5=D1=81=D0=BA=D0=B0=D1=8F=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=B4=D1=83=D0=B1=D0=BB=D0=B8=D1=80=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=D0=B0=20con?= =?UTF-8?q?vertDateFormat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Удалено дублирование метода convertDateFormat в DataService.php - Метод был объявлен дважды, что вызывало PHP Fatal Error - Fatal Error приводил к выводу HTML вместо JSON, вызывая ошибку парсинга в Tabulator - Исправлена ошибка "SyntaxError: The string did not match the expected pattern" --- src/DataService.php | 243 +++++++++++++++++++------------------------- 1 file changed, 106 insertions(+), 137 deletions(-) diff --git a/src/DataService.php b/src/DataService.php index 0df7da5..120f5ff 100644 --- a/src/DataService.php +++ b/src/DataService.php @@ -235,152 +235,121 @@ class DataService return ['deleted' => $stmt->rowCount()]; } -public function insertMultipleRows(string $schema, string $table, array $rows, array $columns): array -{ - error_log("=== ИМПОРТ CSV: Schema=$schema, Table=$table, Строк=" . count($rows) . " ==="); - - if (empty($rows)) { - return ['inserted' => 0, 'errors' => 0, 'message' => 'No rows provided']; - } - - $inserted = 0; - $errors = 0; - $errorMessages = []; - - // Собираем информацию о всех столбцах таблицы - $validColumns = []; - foreach ($columns as $c) { - $name = $c['COLUMN_NAME']; - $extra = $c['EXTRA'] ?? ''; - $dataType = strtolower($c['DATA_TYPE'] ?? ''); + public function insertMultipleRows(string $schema, string $table, array $rows, array $columns): array + { + error_log("=== ИМПОРТ CSV: Schema=$schema, Table=$table, Строк=" . count($rows) . " ==="); - $isNullable = ($c['IS_NULLABLE'] === true || $c['IS_NULLABLE'] === 'YES'); - - // Пропускаем только auto_increment поля - if (!str_contains($extra, 'auto_increment')) { - $validColumns[$name] = [ - 'nullable' => $isNullable, - 'has_default' => !empty($c['COLUMN_DEFAULT']) || $c['COLUMN_DEFAULT'] === '0', - 'default' => $c['COLUMN_DEFAULT'] ?? null, - 'data_type' => $dataType - ]; + if (empty($rows)) { + return ['inserted' => 0, 'errors' => 0, 'message' => 'No rows provided']; } - } - $this->pdo->beginTransaction(); + $inserted = 0; + $errors = 0; + $errorMessages = []; - try { - foreach ($rows as $index => $row) { - try { - $insertCols = []; - $placeholders = []; - $params = []; - - // Перебираем ВСЕ столбцы таблицы (кроме auto_increment) - foreach ($validColumns as $name => $info) { - $value = null; - - // Если столбец есть в CSV строке - if (array_key_exists($name, $row)) { - $value = $row[$name]; - - // Обрабатываем пустые значения как NULL - if ($value === null || $value === '' || in_array($value, ['NULL', 'null'], true)) { - $value = null; - } else { - // Конвертируем форматы дат для date/datetime полей - if (in_array($info['data_type'], ['date', 'datetime', 'timestamp'])) { - $value = $this->convertDateFormat($value); - } - } - } else { - // Столбца нет в CSV - if ($info['has_default']) { - continue; - } - if ($info['nullable']) { - $value = null; - } else { - throw new \PDOException("Missing required field: $name"); - } - } - - $insertCols[] = "`$name`"; - $placeholders[] = ":$name"; - $params[":$name"] = $value; - } - - if (empty($insertCols)) { - continue; - } - - $sql = sprintf( - "INSERT INTO `%s`.`%s` (%s) VALUES (%s)", - $schema, $table, - implode(',', $insertCols), - implode(',', $placeholders) - ); - - $stmt = $this->pdo->prepare($sql); - $stmt->execute($params); - $inserted++; - } catch (\PDOException $e) { - $errors++; - $errorMsg = "Строка " . ($index + 1) . ": " . $this->formatPDOError($e, $schema, $table); - $errorMessages[] = $errorMsg; - - // Логируем только первые 10 ошибок, чтобы не засорять лог - if ($errors <= 10) { - error_log("Ошибка импорта строки " . ($index + 1) . ": " . $e->getMessage()); - } + // Собираем информацию о всех столбцах таблицы + $validColumns = []; + foreach ($columns as $c) { + $name = $c['COLUMN_NAME']; + $extra = $c['EXTRA'] ?? ''; + $dataType = strtolower($c['DATA_TYPE'] ?? ''); + + $isNullable = ($c['IS_NULLABLE'] === true || $c['IS_NULLABLE'] === 'YES'); + + // Пропускаем только auto_increment поля + if (!str_contains($extra, 'auto_increment')) { + $validColumns[$name] = [ + 'nullable' => $isNullable, + 'has_default' => !empty($c['COLUMN_DEFAULT']) || $c['COLUMN_DEFAULT'] === '0', + 'default' => $c['COLUMN_DEFAULT'] ?? null, + 'data_type' => $dataType + ]; } } - $this->pdo->commit(); - error_log("=== ИМПОРТ ЗАВЕРШЁН: вставлено=$inserted, ошибок=$errors ==="); - } catch (\Exception $e) { - $this->pdo->rollBack(); - error_log("=== КРИТИЧЕСКАЯ ОШИБКА ИМПОРТА: " . $e->getMessage() . " ==="); - throw new \RuntimeException('Import failed: ' . $e->getMessage()); - } + $this->pdo->beginTransaction(); - return [ - 'inserted' => $inserted, - 'errors' => $errors, - 'errorMessages' => $errorMessages - ]; -} + try { + foreach ($rows as $index => $row) { + try { + $insertCols = []; + $placeholders = []; + $params = []; -/** - * Конвертирует различные форматы дат в формат MySQL (YYYY-MM-DD) - */ -private function convertDateFormat(string $date): string -{ - $date = trim($date); - - // Уже в нужном формате YYYY-MM-DD - if (preg_match('/^\d{4}-\d{2}-\d{2}/', $date)) { - return substr($date, 0, 10); - } - - // Формат DD.MM.YYYY или DD/MM/YYYY или DD-MM-YYYY - if (preg_match('/^(\d{1,2})[\.\/-](\d{1,2})[\.\/-](\d{4})/', $date, $matches)) { - $day = str_pad($matches[1], 2, '0', STR_PAD_LEFT); - $month = str_pad($matches[2], 2, '0', STR_PAD_LEFT); - $year = $matches[3]; - return "$year-$month-$day"; - } - - // Пробуем распарсить через strtotime - $timestamp = strtotime($date); - if ($timestamp !== false) { - return date('Y-m-d', $timestamp); - } - - // Если не удалось распарсить - возвращаем как есть - return $date; -} + // Перебираем ВСЕ столбцы таблицы (кроме auto_increment) + foreach ($validColumns as $name => $info) { + $value = null; + + // Если столбец есть в CSV строке + if (array_key_exists($name, $row)) { + $value = $row[$name]; + + // Обрабатываем пустые значения как NULL + if ($value === null || $value === '' || in_array($value, ['NULL', 'null'], true)) { + $value = null; + } else { + // Конвертируем форматы дат для date/datetime полей + if (in_array($info['data_type'], ['date', 'datetime', 'timestamp'])) { + $value = $this->convertDateFormat($value); + } + } + } else { + // Столбца нет в CSV + if ($info['has_default']) { + continue; + } + if ($info['nullable']) { + $value = null; + } else { + throw new \PDOException("Missing required field: $name"); + } + } + + $insertCols[] = "`$name`"; + $placeholders[] = ":$name"; + $params[":$name"] = $value; + } + if (empty($insertCols)) { + continue; + } + + $sql = sprintf( + "INSERT INTO `%s`.`%s` (%s) VALUES (%s)", + $schema, $table, + implode(',', $insertCols), + implode(',', $placeholders) + ); + + $stmt = $this->pdo->prepare($sql); + $stmt->execute($params); + $inserted++; + } catch (\PDOException $e) { + $errors++; + $errorMsg = "Строка " . ($index + 1) . ": " . $this->formatPDOError($e, $schema, $table); + $errorMessages[] = $errorMsg; + + // Логируем только первые 10 ошибок + if ($errors <= 10) { + error_log("Ошибка импорта строки " . ($index + 1) . ": " . $e->getMessage()); + } + } + } + + $this->pdo->commit(); + error_log("=== ИМПОРТ ЗАВЕРШЁН: вставлено=$inserted, ошибок=$errors ==="); + } catch (\Exception $e) { + $this->pdo->rollBack(); + error_log("=== КРИТИЧЕСКАЯ ОШИБКА ИМПОРТА: " . $e->getMessage() . " ==="); + throw new \RuntimeException('Import failed: ' . $e->getMessage()); + } + + return [ + 'inserted' => $inserted, + 'errors' => $errors, + 'errorMessages' => $errorMessages + ]; + } /** * Конвертирует различные форматы дат в формат MySQL (YYYY-MM-DD) @@ -408,7 +377,7 @@ private function convertDateFormat(string $date): string return date('Y-m-d', $timestamp); } - // Если не удалось распарсить - возвращаем как есть (будет ошибка от БД) + // Если не удалось распарсить - возвращаем как есть return $date; }