From 2f7a1805438388040fed056597d7a5bd7b325514 Mon Sep 17 00:00:00 2001 From: Mikhail Chusavitin Date: Wed, 21 Jan 2026 18:32:53 +0300 Subject: [PATCH] add dropdown menu for FK values --- public/app.js | 82 ++++++++++++++++++++++++++++++++++++++++----- src/DataService.php | 29 ++++++++-------- 2 files changed, 88 insertions(+), 23 deletions(-) diff --git a/public/app.js b/public/app.js index e7ff348..118a58c 100644 --- a/public/app.js +++ b/public/app.js @@ -205,6 +205,28 @@ async function selectTable(schema, tableName) { console.log('📋 Метаданные получены:', currentMeta); + // ✅ Загружаем значения для всех FK полей + const fkValues = new Map(); + + for (const col of currentMeta.columns) { + if (col.IS_FOREIGN_KEY && col.FOREIGN_KEY) { + console.log('🔗 Загрузка FK значений для:', col.COLUMN_NAME); + try { + const result = await api( + `/api/fk-values?schema=${encodeURIComponent(col.FOREIGN_KEY.ref_schema)}&` + + `table=${encodeURIComponent(col.FOREIGN_KEY.ref_table)}&` + + `column=${encodeURIComponent(col.FOREIGN_KEY.ref_column)}` + ); + fkValues.set(col.COLUMN_NAME, result.values || []); + console.log(' ✅ Загружено значений:', result.values.length); + } catch (err) { + console.error(' ❌ Ошибка загрузки FK:', err); + fkValues.set(col.COLUMN_NAME, []); + } + } + } + + // ✅ Формируем колонки с правильными редакторами const columns = [ { formatter: "rowSelection", @@ -217,12 +239,45 @@ async function selectTable(schema, tableName) { cell.getRow().toggleSelect(); } }, - ...currentMeta.columns.map(col => ({ - title: col.COLUMN_NAME, - field: col.COLUMN_NAME, - editor: "input", - headerFilter: "input" - })) + ...currentMeta.columns.map(col => { + const colDef = { + title: col.COLUMN_NAME, + field: col.COLUMN_NAME, + headerFilter: "input" + }; + + // ✅ Выбираем редактор в зависимости от типа поля + if (col.IS_FOREIGN_KEY && fkValues.has(col.COLUMN_NAME)) { + const values = fkValues.get(col.COLUMN_NAME); + + if (values.length > 0) { + // ✅ Выпадающий список для FK + colDef.editor = "list"; + colDef.editorParams = { + values: values, + clearable: col.IS_NULLABLE, // Разрешаем очистку для nullable полей + autocomplete: true, + listOnEmpty: true, + freetext: false // Запрещаем ввод произвольного текста + }; + + console.log(` 📋 ${col.COLUMN_NAME}: select с ${values.length} значениями`); + } else { + // Нет значений - обычный input с предупреждением + colDef.editor = "input"; + colDef.editorParams = { + elementAttributes: { + placeholder: `⚠️ Нет значений в ${col.FOREIGN_KEY.ref_table}` + } + }; + } + } else { + // Обычный редактор + colDef.editor = "input"; + } + + return colDef; + }) ]; if (table) { @@ -324,11 +379,19 @@ async function selectTable(schema, tableName) { } catch (err) { console.error('❌ ОШИБКА:', err); + // ✅ Красная подсветка + откат значения if (rowElement && rowElement.getElement) { rowElement.getElement().style.backgroundColor = '#ffebee'; + + // Подсветка остаётся до перезагрузки данных + setTimeout(() => { + if (confirm('Ошибка сохранения:\n' + err.message + '\n\nОбновить таблицу?')) { + table.replaceData(); + } + }, 100); + } else { + alert('Ошибка: ' + err.message); } - - alert('Ошибка: ' + err.message); } } @@ -412,7 +475,10 @@ async function selectTable(schema, tableName) { document.addEventListener('keydown', enterHandler); console.log('✅ ВСЕ СОБЫТИЯ ПОДКЛЮЧЕНЫ - ВЕРСИЯ 2.0'); + console.log('📋 FK полей с dropdown:', Array.from(fkValues.keys()).join(', ')); } + + // functions document.getElementById('btnSelectAll').addEventListener('click', async () => { diff --git a/src/DataService.php b/src/DataService.php index 1f25b7f..2390369 100644 --- a/src/DataService.php +++ b/src/DataService.php @@ -197,10 +197,7 @@ private function formatPDOError(\PDOException $e, string $schema, string $table, public function updateRow(string $schema, string $table, array $row, array $columns, array $pk): array { error_log("=== UPDATE ROW ==="); - error_log("Schema: $schema"); - error_log("Table: $table"); - error_log("Row data: " . json_encode($row, JSON_UNESCAPED_UNICODE)); - error_log("PK: " . json_encode($pk)); + error_log("Schema: $schema, Table: $table"); if (empty($pk)) { throw new \RuntimeException('No primary key — update disabled'); @@ -221,14 +218,12 @@ public function updateRow(string $schema, string $table, array $row, array $colu } if (empty($sets)) { - error_log("No changes to update"); return ['updated' => 0, 'message' => 'No changes']; } $whereParts = []; foreach ($pk as $name) { if (!array_key_exists($name, $row)) { - error_log("ERROR: Missing PK value: $name"); throw new \RuntimeException("Missing PK value: $name"); } $whereParts[] = "`$name` = :pk_$name"; @@ -242,20 +237,24 @@ public function updateRow(string $schema, string $table, array $row, array $colu implode(' AND ', $whereParts) ); - error_log("SQL: $sql"); - error_log("Params: " . json_encode($params, JSON_UNESCAPED_UNICODE)); - $stmt = $this->pdo->prepare($sql); - $stmt->execute($params); - $rowCount = $stmt->rowCount(); - error_log("Rows updated: $rowCount"); - error_log("=== UPDATE COMPLETE ==="); - - return ['updated' => $rowCount]; + // ✅ Обрабатываем ошибки с красивым сообщением + try { + $stmt->execute($params); + $rowCount = $stmt->rowCount(); + error_log("Rows updated: $rowCount"); + return ['updated' => $rowCount]; + } catch (\PDOException $e) { + error_log("UPDATE FAILED: " . $e->getMessage()); + + // Форматируем ошибку для пользователя + throw new \RuntimeException($this->formatPDOError($e, $schema, $table, $params)); + } } + public function deleteRow(string $schema, string $table, array $row, array $pk): array { if (empty($pk)) {