Implement row deletion and insertion functionality in Adminer UI
This commit is contained in:
parent
4dbfffe91e
commit
5af409792d
@ -255,6 +255,96 @@ try {
|
||||
}
|
||||
}
|
||||
|
||||
// Datensatz löschen
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && (string)($_POST['action'] ?? '') === 'delete_row') {
|
||||
$delTable = (string)($_POST['table'] ?? '');
|
||||
$pkCol = (string)($_POST['pk_col'] ?? '');
|
||||
$pkVal = (string)($_POST['pk_val'] ?? '');
|
||||
|
||||
if (!preg_match('/^[A-Za-z0-9_]+$/', $delTable) || !preg_match('/^[A-Za-z0-9_]+$/', $pkCol)) {
|
||||
$msg = ['ok' => false, 'text' => 'Ungültige Parameter für Löschen.'];
|
||||
} else {
|
||||
try {
|
||||
// PK-Spalte gegen echte PK-Spalte validieren
|
||||
$realPk = admin_get_primary_key_column($pdo, $delTable);
|
||||
if ($realPk === null || $realPk !== $pkCol) {
|
||||
$msg = ['ok' => false, 'text' => 'Löschen ist nur über eine echte Primary-Key-Spalte möglich.'];
|
||||
} else {
|
||||
$stmt = $pdo->prepare('DELETE FROM `' . $delTable . '` WHERE `' . $pkCol . '` = :v LIMIT 1');
|
||||
$stmt->execute([':v' => $pkVal]);
|
||||
$msg = ['ok' => true, 'text' => 'Datensatz gelöscht.'];
|
||||
|
||||
// Wenn wir gerade diese Tabelle anzeigen: auf Seite 1 zurück, damit man nicht auf leerer Seite landet
|
||||
if ($table === $delTable) {
|
||||
$page = 1;
|
||||
$offset = 0;
|
||||
}
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
$msg = ['ok' => false, 'text' => $e->getMessage()];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Datensatz hinzufügen
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && (string)($_POST['action'] ?? '') === 'insert_row') {
|
||||
$insTable = (string)($_POST['table'] ?? '');
|
||||
if (!preg_match('/^[A-Za-z0-9_]+$/', $insTable)) {
|
||||
$msg = ['ok' => false, 'text' => 'Ungültiger Tabellenname für Insert.'];
|
||||
} else {
|
||||
try {
|
||||
$colsMeta = admin_get_table_columns($pdo, $insTable);
|
||||
if (empty($colsMeta)) {
|
||||
$msg = ['ok' => false, 'text' => 'Keine Spalten gefunden.'];
|
||||
} else {
|
||||
$fields = [];
|
||||
foreach ($colsMeta as $c) {
|
||||
$name = (string)$c['Field'];
|
||||
// Nur Spalten zulassen, die wirklich existieren
|
||||
if (!array_key_exists('col_' . $name, $_POST)) continue;
|
||||
|
||||
$raw = (string)($_POST['col_' . $name] ?? '');
|
||||
$isNull = ((string)($_POST['null_' . $name] ?? '') === '1');
|
||||
|
||||
$fields[] = [
|
||||
'name' => $name,
|
||||
'value' => $isNull ? null : ($raw === '' ? null : $raw),
|
||||
'forceNull' => $isNull,
|
||||
];
|
||||
}
|
||||
|
||||
// Leere Submits verhindern
|
||||
if (empty($fields)) {
|
||||
$msg = ['ok' => false, 'text' => 'Keine Felder zum Einfügen übergeben.'];
|
||||
} else {
|
||||
$colNames = [];
|
||||
$placeholders = [];
|
||||
$params = [];
|
||||
|
||||
foreach ($fields as $f) {
|
||||
$colNames[] = '`' . $f['name'] . '`';
|
||||
$ph = ':c_' . $f['name'];
|
||||
$placeholders[] = $ph;
|
||||
$params[$ph] = $f['value'];
|
||||
}
|
||||
|
||||
$sql = 'INSERT INTO `' . $insTable . '` (' . implode(',', $colNames) . ') VALUES (' . implode(',', $placeholders) . ')';
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
$msg = ['ok' => true, 'text' => 'Datensatz hinzugefügt.'];
|
||||
|
||||
// Nach Insert wieder zur Tabelle springen
|
||||
$table = $insTable;
|
||||
$page = 1;
|
||||
$offset = 0;
|
||||
}
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
$msg = ['ok' => false, 'text' => $e->getMessage()];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$tables = $pdo->query('SHOW TABLES')->fetchAll(PDO::FETCH_NUM);
|
||||
|
||||
// ── TOP BAR ──────────────────────────────────────────────────────────
|
||||
@ -305,18 +395,70 @@ try {
|
||||
if (!preg_match('/^[A-Za-z0-9_]+$/', $table)) {
|
||||
$body .= '<div class="notice notice-err">Ungültiger Tabellenname.</div>';
|
||||
} else {
|
||||
$pkCol = admin_get_primary_key_column($pdo, $table);
|
||||
$stmt = $pdo->query('SELECT * FROM `' . $table . '` LIMIT ' . (int)$limit . ' OFFSET ' . (int)$offset);
|
||||
$rows = $stmt->fetchAll();
|
||||
|
||||
$body .= '<div class="card" style="margin-bottom:16px">';
|
||||
$body .= '<h2>' . h($table) . '</h2>';
|
||||
$body .= admin_render_table($rows);
|
||||
$body .= '<div style="display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap">'
|
||||
. '<h2 style="margin:0">' . h($table) . '</h2>'
|
||||
. '<a class="btn btn-ghost btn-sm" href="/adminer?t=' . rawurlencode($table) . '&add=1">+ Datensatz</a>'
|
||||
. '</div>';
|
||||
|
||||
$body .= admin_render_table($rows, $table, $pkCol);
|
||||
|
||||
$body .= '<div class="pagination">';
|
||||
if ($page > 1) $body .= '<a class="btn btn-ghost btn-sm" href="/adminer?t=' . rawurlencode($table) . '&p=' . ($page - 1) . '">← Zurück</a>';
|
||||
if (count($rows) === $limit) $body .= '<a class="btn btn-ghost btn-sm" href="/adminer?t=' . rawurlencode($table) . '&p=' . ($page + 1) . '">Weiter →</a>';
|
||||
if ($page > 1 || count($rows) === $limit) $body .= '<span class="muted">Seite ' . $page . '</span>';
|
||||
$body .= '</div>';
|
||||
|
||||
$body .= '</div>';
|
||||
|
||||
// Add form
|
||||
if ((string)($_GET['add'] ?? '') === '1') {
|
||||
$colsMeta = admin_get_table_columns($pdo, $table);
|
||||
$body .= '<div class="card" style="margin-bottom:16px">';
|
||||
$body .= '<h2>Datensatz hinzufügen</h2>';
|
||||
$body .= '<form method="post">'
|
||||
. '<input type="hidden" name="action" value="insert_row">'
|
||||
. '<input type="hidden" name="table" value="' . h($table) . '">';
|
||||
|
||||
foreach ($colsMeta as $c) {
|
||||
$col = (string)$c['Field'];
|
||||
$extra = (string)($c['Extra'] ?? '');
|
||||
$nullOk = ((string)($c['Null'] ?? 'NO') === 'YES');
|
||||
|
||||
// auto_increment standardmäßig nicht anfassen
|
||||
if (stripos($extra, 'auto_increment') !== false) {
|
||||
$body .= '<div class="field">'
|
||||
. '<label>' . h($col) . ' <span class="pill">auto</span></label>'
|
||||
. '<input disabled value="(auto)" />'
|
||||
. '</div>';
|
||||
continue;
|
||||
}
|
||||
|
||||
$body .= '<div class="field">'
|
||||
. '<label>' . h($col) . ($nullOk ? ' <span class="pill">NULL ok</span>' : '') . '</label>'
|
||||
. '<input name="col_' . h($col) . '" placeholder="Wert…">';
|
||||
|
||||
if ($nullOk) {
|
||||
$body .= '<div style="margin-top:8px;display:flex;align-items:center;gap:8px">'
|
||||
. '<input style="width:auto" type="checkbox" name="null_' . h($col) . '" value="1" id="null_' . h($col) . '">'
|
||||
. '<label for="null_' . h($col) . '" style="margin:0;text-transform:none;letter-spacing:0;font-size:.85rem;color:var(--text-muted)">NULL setzen</label>'
|
||||
. '</div>';
|
||||
}
|
||||
|
||||
$body .= '</div>';
|
||||
}
|
||||
|
||||
$body .= '<div style="display:flex;gap:10px;flex-wrap:wrap">'
|
||||
. '<button class="btn" type="submit">Speichern</button>'
|
||||
. '<a class="btn btn-ghost" href="/adminer?t=' . rawurlencode($table) . '">Abbrechen</a>'
|
||||
. '</div>'
|
||||
. '</form>';
|
||||
$body .= '</div>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -345,13 +487,17 @@ try {
|
||||
);
|
||||
}
|
||||
|
||||
function admin_render_table(array $rows): string
|
||||
function admin_render_table(array $rows, string $table = '', ?string $pkCol = null): string
|
||||
{
|
||||
if (empty($rows)) return '<p class="muted">(keine Zeilen)</p>';
|
||||
|
||||
$cols = array_keys((array)$rows[0]);
|
||||
$hasActions = ($table !== '' && $pkCol !== null && in_array($pkCol, $cols, true));
|
||||
|
||||
$html = '<div style="overflow-x:auto"><table class="db-table"><thead><tr>';
|
||||
foreach ($cols as $c) $html .= '<th>' . h((string)$c) . '</th>';
|
||||
if ($hasActions) $html .= '<th style="width:1%;white-space:nowrap">Aktionen</th>';
|
||||
|
||||
$html .= '</tr></thead><tbody>';
|
||||
foreach ($rows as $r) {
|
||||
$html .= '<tr>';
|
||||
@ -362,9 +508,50 @@ function admin_render_table(array $rows): string
|
||||
: (strlen((string)$v) > 300 ? h(substr((string)$v, 0, 300)) . '…' : h((string)$v));
|
||||
$html .= '<td>' . $cell . '</td>';
|
||||
}
|
||||
|
||||
if ($hasActions) {
|
||||
$pkVal = $r[$pkCol] ?? null;
|
||||
$safePkVal = $pkVal === null ? '' : (string)$pkVal;
|
||||
|
||||
$html .= '<td>'
|
||||
. '<form method="post" style="display:inline" onsubmit="return confirm(\'Datensatz wirklich löschen?\');">'
|
||||
. '<input type="hidden" name="action" value="delete_row">'
|
||||
. '<input type="hidden" name="table" value="' . h($table) . '">'
|
||||
. '<input type="hidden" name="pk_col" value="' . h((string)$pkCol) . '">'
|
||||
. '<input type="hidden" name="pk_val" value="' . h($safePkVal) . '">'
|
||||
. '<button class="btn btn-ghost btn-sm" type="submit">Löschen</button>'
|
||||
. '</form>'
|
||||
. '</td>';
|
||||
}
|
||||
|
||||
$html .= '</tr>';
|
||||
}
|
||||
$html .= '</tbody></table></div>';
|
||||
return $html;
|
||||
}
|
||||
|
||||
function admin_get_primary_key_column(PDO $pdo, string $table): ?string
|
||||
{
|
||||
if (!preg_match('/^[A-Za-z0-9_]+$/', $table)) return null;
|
||||
|
||||
$stmt = $pdo->prepare(
|
||||
"SELECT COLUMN_NAME\n"
|
||||
. "FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE\n"
|
||||
. "WHERE TABLE_SCHEMA = DATABASE()\n"
|
||||
. " AND TABLE_NAME = :t\n"
|
||||
. " AND CONSTRAINT_NAME = 'PRIMARY'\n"
|
||||
. "ORDER BY ORDINAL_POSITION\n"
|
||||
. "LIMIT 1"
|
||||
);
|
||||
$stmt->execute([':t' => $table]);
|
||||
$pk = $stmt->fetchColumn();
|
||||
return $pk !== false ? (string)$pk : null;
|
||||
}
|
||||
|
||||
function admin_get_table_columns(PDO $pdo, string $table): array
|
||||
{
|
||||
if (!preg_match('/^[A-Za-z0-9_]+$/', $table)) return [];
|
||||
$stmt = $pdo->query('SHOW COLUMNS FROM `' . $table . '`');
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
return is_array($rows) ? $rows : [];
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user