fix: исправлен импорт CSV с неполным набором столбцов

- Добавлена поддержка импорта CSV файлов с отсутствующими столбцами
- Для отсутствующих столбцов устанавливается NULL (если nullable) или значение по умолчанию
- Автоинкрементные поля корректно пропускаются, счетчик увеличивается автоматически
- Улучшена обработка NULL значений ("NULL", "null", "") - конвертируются в реальный NULL
- Добавлена валидация обязательных полей без значений по умолчанию
This commit is contained in:
2026-01-21 03:31:26 +03:00
parent 5eb3cd432e
commit fff4e80ffd

View File

@@ -239,76 +239,107 @@ 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
{ {
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'] ?? '';
// Пропускаем только auto_increment поля
if (!str_contains($extra, 'auto_increment')) {
$validColumns[$name] = [
'nullable' => $c['IS_NULLABLE'] ?? false,
'has_default' => !empty($c['COLUMN_DEFAULT']) || $c['COLUMN_DEFAULT'] === '0',
'default' => $c['COLUMN_DEFAULT'] ?? null
];
} }
}
$inserted = 0; $this->pdo->beginTransaction();
$errors = 0;
$errorMessages = [];
$validColumns = []; try {
foreach ($columns as $c) { foreach ($rows as $index => $row) {
$name = $c['COLUMN_NAME']; try {
$extra = $c['EXTRA'] ?? ''; $insertCols = [];
$placeholders = [];
$params = [];
if (!str_contains($extra, 'auto_increment')) { // ✅ Перебираем ВСЕ столбцы таблицы (кроме auto_increment)
$validColumns[] = $name; foreach ($validColumns as $name => $info) {
} $value = null;
}
$this->pdo->beginTransaction(); // Если столбец есть в CSV строке
if (array_key_exists($name, $row)) {
$value = $row[$name];
try { // ✅ Обрабатываем "NULL" как NULL
foreach ($rows as $index => $row) { if (in_array($value, ['NULL', 'null', ''], true)) {
try { $value = null;
$insertCols = []; }
$placeholders = []; } else {
$params = []; // ✅ Столбца нет в CSV - устанавливаем NULL или пропускаем
// Если поле имеет значение по умолчанию - пропускаем (БД сама подставит)
foreach ($validColumns as $name) { if ($info['has_default']) {
if (array_key_exists($name, $row)) { continue;
$insertCols[] = "`$name`"; }
$placeholders[] = ":$name"; // Если поле nullable - ставим NULL
$params[":$name"] = $row[$name]; if ($info['nullable']) {
$value = null;
} else {
// Поле обязательное и нет дефолта - ошибка
throw new \PDOException("Missing required field: $name");
} }
} }
if (empty($insertCols)) { $insertCols[] = "`$name`";
continue; $placeholders[] = ":$name";
} $params[":$name"] = $value;
$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++;
$errorMessages[] = "Строка " . ($index + 1) . ": " . $this->formatPDOError($e, $schema, $table);
} }
}
$this->pdo->commit(); if (empty($insertCols)) {
} catch (\Exception $e) { continue;
$this->pdo->rollBack(); }
throw new \RuntimeException('Import failed: ' . $e->getMessage());
$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++;
$errorMessages[] = "Строка " . ($index + 1) . ": " . $this->formatPDOError($e, $schema, $table);
}
} }
return [ $this->pdo->commit();
'inserted' => $inserted, } catch (\Exception $e) {
'errors' => $errors, $this->pdo->rollBack();
'errorMessages' => $errorMessages throw new \RuntimeException('Import failed: ' . $e->getMessage());
];
} }
return [
'inserted' => $inserted,
'errors' => $errors,
'errorMessages' => $errorMessages
];
}
public function exportCSV( public function exportCSV(
string $schema, string $schema,
string $table, string $table,