fix: исправлена критическая ошибка дублирования метода convertDateFormat

- Удалено дублирование метода convertDateFormat в DataService.php
- Метод был объявлен дважды, что вызывало PHP Fatal Error
- Fatal Error приводил к выводу HTML вместо JSON, вызывая ошибку парсинга в Tabulator
- Исправлена ошибка "SyntaxError: The string did not match the expected pattern"
This commit is contained in:
2026-01-21 04:08:13 +03:00
parent 130f63f6b2
commit de5266f98f

View File

@@ -235,153 +235,122 @@ class DataService
return ['deleted' => $stmt->rowCount()]; return ['deleted' => $stmt->rowCount()];
} }
public function insertMultipleRows(string $schema, string $table, array $rows, array $columns): array public function insertMultipleRows(string $schema, string $table, array $rows, array $columns): array
{ {
error_log("=== ИМПОРТ CSV: Schema=$schema, Table=$table, Строк=" . count($rows) . " ==="); error_log("=== ИМПОРТ CSV: Schema=$schema, Table=$table, Строк=" . count($rows) . " ===");
if (empty($rows)) { if (empty($rows)) {
return ['inserted' => 0, 'errors' => 0, 'message' => 'No rows provided']; 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'] ?? '');
$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->beginTransaction(); $inserted = 0;
$errors = 0;
$errorMessages = [];
try { // Собираем информацию о всех столбцах таблицы
foreach ($rows as $index => $row) { $validColumns = [];
try { foreach ($columns as $c) {
$insertCols = []; $name = $c['COLUMN_NAME'];
$placeholders = []; $extra = $c['EXTRA'] ?? '';
$params = []; $dataType = strtolower($c['DATA_TYPE'] ?? '');
// Перебираем ВСЕ столбцы таблицы (кроме auto_increment) $isNullable = ($c['IS_NULLABLE'] === true || $c['IS_NULLABLE'] === 'YES');
foreach ($validColumns as $name => $info) {
$value = null;
// Если столбец есть в CSV строке // Пропускаем только auto_increment поля
if (array_key_exists($name, $row)) { if (!str_contains($extra, 'auto_increment')) {
$value = $row[$name]; $validColumns[$name] = [
'nullable' => $isNullable,
// Обрабатываем пустые значения как NULL 'has_default' => !empty($c['COLUMN_DEFAULT']) || $c['COLUMN_DEFAULT'] === '0',
if ($value === null || $value === '' || in_array($value, ['NULL', 'null'], true)) { 'default' => $c['COLUMN_DEFAULT'] ?? null,
$value = null; 'data_type' => $dataType
} 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(); $this->pdo->beginTransaction();
error_log("=== ИМПОРТ ЗАВЕРШЁН: вставлено=$inserted, ошибок=$errors ===");
} catch (\Exception $e) { try {
$this->pdo->rollBack(); foreach ($rows as $index => $row) {
error_log("=== КРИТИЧЕСКАЯ ОШИБКА ИМПОРТА: " . $e->getMessage() . " ==="); try {
throw new \RuntimeException('Import failed: ' . $e->getMessage()); $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());
}
}
}
$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
];
} }
return [
'inserted' => $inserted,
'errors' => $errors,
'errorMessages' => $errorMessages
];
}
/**
* Конвертирует различные форматы дат в формат 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;
}
/** /**
* Конвертирует различные форматы дат в формат MySQL (YYYY-MM-DD) * Конвертирует различные форматы дат в формат MySQL (YYYY-MM-DD)
*/ */
@@ -408,7 +377,7 @@ private function convertDateFormat(string $date): string
return date('Y-m-d', $timestamp); return date('Y-m-d', $timestamp);
} }
// Если не удалось распарсить - возвращаем как есть (будет ошибка от БД) // Если не удалось распарсить - возвращаем как есть
return $date; return $date;
} }