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:
@@ -235,152 +235,121 @@ 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)) {
|
|
||||||
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');
|
if (empty($rows)) {
|
||||||
|
return ['inserted' => 0, 'errors' => 0, 'message' => 'No rows provided'];
|
||||||
// Пропускаем только 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;
|
// Пропускаем только auto_increment поля
|
||||||
|
if (!str_contains($extra, 'auto_increment')) {
|
||||||
// Если столбец есть в CSV строке
|
$validColumns[$name] = [
|
||||||
if (array_key_exists($name, $row)) {
|
'nullable' => $isNullable,
|
||||||
$value = $row[$name];
|
'has_default' => !empty($c['COLUMN_DEFAULT']) || $c['COLUMN_DEFAULT'] === '0',
|
||||||
|
'default' => $c['COLUMN_DEFAULT'] ?? null,
|
||||||
// Обрабатываем пустые значения как NULL
|
'data_type' => $dataType
|
||||||
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();
|
$this->pdo->beginTransaction();
|
||||||
error_log("=== ИМПОРТ ЗАВЕРШЁН: вставлено=$inserted, ошибок=$errors ===");
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$this->pdo->rollBack();
|
|
||||||
error_log("=== КРИТИЧЕСКАЯ ОШИБКА ИМПОРТА: " . $e->getMessage() . " ===");
|
|
||||||
throw new \RuntimeException('Import failed: ' . $e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
try {
|
||||||
'inserted' => $inserted,
|
foreach ($rows as $index => $row) {
|
||||||
'errors' => $errors,
|
try {
|
||||||
'errorMessages' => $errorMessages
|
$insertCols = [];
|
||||||
];
|
$placeholders = [];
|
||||||
}
|
$params = [];
|
||||||
|
|
||||||
/**
|
// Перебираем ВСЕ столбцы таблицы (кроме auto_increment)
|
||||||
* Конвертирует различные форматы дат в формат MySQL (YYYY-MM-DD)
|
foreach ($validColumns as $name => $info) {
|
||||||
*/
|
$value = null;
|
||||||
private function convertDateFormat(string $date): string
|
|
||||||
{
|
// Если столбец есть в CSV строке
|
||||||
$date = trim($date);
|
if (array_key_exists($name, $row)) {
|
||||||
|
$value = $row[$name];
|
||||||
// Уже в нужном формате YYYY-MM-DD
|
|
||||||
if (preg_match('/^\d{4}-\d{2}-\d{2}/', $date)) {
|
// Обрабатываем пустые значения как NULL
|
||||||
return substr($date, 0, 10);
|
if ($value === null || $value === '' || in_array($value, ['NULL', 'null'], true)) {
|
||||||
}
|
$value = null;
|
||||||
|
} else {
|
||||||
// Формат DD.MM.YYYY или DD/MM/YYYY или DD-MM-YYYY
|
// Конвертируем форматы дат для date/datetime полей
|
||||||
if (preg_match('/^(\d{1,2})[\.\/-](\d{1,2})[\.\/-](\d{4})/', $date, $matches)) {
|
if (in_array($info['data_type'], ['date', 'datetime', 'timestamp'])) {
|
||||||
$day = str_pad($matches[1], 2, '0', STR_PAD_LEFT);
|
$value = $this->convertDateFormat($value);
|
||||||
$month = str_pad($matches[2], 2, '0', STR_PAD_LEFT);
|
}
|
||||||
$year = $matches[3];
|
}
|
||||||
return "$year-$month-$day";
|
} else {
|
||||||
}
|
// Столбца нет в CSV
|
||||||
|
if ($info['has_default']) {
|
||||||
// Пробуем распарсить через strtotime
|
continue;
|
||||||
$timestamp = strtotime($date);
|
}
|
||||||
if ($timestamp !== false) {
|
if ($info['nullable']) {
|
||||||
return date('Y-m-d', $timestamp);
|
$value = null;
|
||||||
}
|
} else {
|
||||||
|
throw new \PDOException("Missing required field: $name");
|
||||||
// Если не удалось распарсить - возвращаем как есть
|
}
|
||||||
return $date;
|
}
|
||||||
}
|
|
||||||
|
$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)
|
* Конвертирует различные форматы дат в формат 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user