From fff4e80ffd199f0d2473f673decc975d9168ff00 Mon Sep 17 00:00:00 2001 From: Michael Chus Date: Wed, 21 Jan 2026 03:31:26 +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=20=D0=B8=D0=BC=D0=BF=D0=BE=D1=80=D1=82=20CSV?= =?UTF-8?q?=20=D1=81=20=D0=BD=D0=B5=D0=BF=D0=BE=D0=BB=D0=BD=D1=8B=D0=BC=20?= =?UTF-8?q?=D0=BD=D0=B0=D0=B1=D0=BE=D1=80=D0=BE=D0=BC=20=D1=81=D1=82=D0=BE?= =?UTF-8?q?=D0=BB=D0=B1=D1=86=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавлена поддержка импорта CSV файлов с отсутствующими столбцами - Для отсутствующих столбцов устанавливается NULL (если nullable) или значение по умолчанию - Автоинкрементные поля корректно пропускаются, счетчик увеличивается автоматически - Улучшена обработка NULL значений ("NULL", "null", "") - конвертируются в реальный NULL - Добавлена валидация обязательных полей без значений по умолчанию --- src/DataService.php | 145 +++++++++++++++++++++++++++----------------- 1 file changed, 88 insertions(+), 57 deletions(-) diff --git a/src/DataService.php b/src/DataService.php index 2561ea0..c9b9629 100644 --- a/src/DataService.php +++ b/src/DataService.php @@ -239,76 +239,107 @@ class DataService return ['deleted' => $stmt->rowCount()]; } - public function insertMultipleRows(string $schema, string $table, array $rows, array $columns): array - { - if (empty($rows)) { - return ['inserted' => 0, 'errors' => 0, 'message' => 'No rows provided']; +public function insertMultipleRows(string $schema, string $table, array $rows, array $columns): array +{ + 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'] ?? ''; + + // Пропускаем только 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; - $errors = 0; - $errorMessages = []; + $this->pdo->beginTransaction(); - $validColumns = []; - foreach ($columns as $c) { - $name = $c['COLUMN_NAME']; - $extra = $c['EXTRA'] ?? ''; - - if (!str_contains($extra, 'auto_increment')) { - $validColumns[] = $name; - } - } + try { + foreach ($rows as $index => $row) { + try { + $insertCols = []; + $placeholders = []; + $params = []; - $this->pdo->beginTransaction(); - - try { - foreach ($rows as $index => $row) { - try { - $insertCols = []; - $placeholders = []; - $params = []; - - foreach ($validColumns as $name) { - if (array_key_exists($name, $row)) { - $insertCols[] = "`$name`"; - $placeholders[] = ":$name"; - $params[":$name"] = $row[$name]; + // ✅ Перебираем ВСЕ столбцы таблицы (кроме auto_increment) + foreach ($validColumns as $name => $info) { + $value = null; + + // Если столбец есть в CSV строке + if (array_key_exists($name, $row)) { + $value = $row[$name]; + + // ✅ Обрабатываем "NULL" как NULL + if (in_array($value, ['NULL', 'null', ''], true)) { + $value = null; + } + } else { + // ✅ Столбца нет в CSV - устанавливаем NULL или пропускаем + // Если поле имеет значение по умолчанию - пропускаем (БД сама подставит) + if ($info['has_default']) { + continue; + } + // Если поле nullable - ставим NULL + if ($info['nullable']) { + $value = null; + } else { + // Поле обязательное и нет дефолта - ошибка + throw new \PDOException("Missing required field: $name"); } } - - 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++; - $errorMessages[] = "Строка " . ($index + 1) . ": " . $this->formatPDOError($e, $schema, $table); + $insertCols[] = "`$name`"; + $placeholders[] = ":$name"; + $params[":$name"] = $value; } - } - $this->pdo->commit(); - } catch (\Exception $e) { - $this->pdo->rollBack(); - throw new \RuntimeException('Import failed: ' . $e->getMessage()); + 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++; + $errorMessages[] = "Строка " . ($index + 1) . ": " . $this->formatPDOError($e, $schema, $table); + } } - return [ - 'inserted' => $inserted, - 'errors' => $errors, - 'errorMessages' => $errorMessages - ]; + $this->pdo->commit(); + } catch (\Exception $e) { + $this->pdo->rollBack(); + throw new \RuntimeException('Import failed: ' . $e->getMessage()); } + return [ + 'inserted' => $inserted, + 'errors' => $errors, + 'errorMessages' => $errorMessages + ]; +} + + public function exportCSV( string $schema, string $table,