{
"event": "PreToolUse",
"tool_name": "Edit",
"tool_input": {
"file_path": "\/var\/www\/dev.campus.systemische-tools.de\/src\/Controller\/ConfigController.php",
"old_string": "<?php\n\nnamespace Controller;\n\nuse Framework\\Controller;\nuse Infrastructure\\Config\\DatabaseFactory;\n\nclass ConfigController extends Controller\n{\n private \\PDO $db;\n\n private const TYPES = ['author_profile', 'structure', 'organization', 'contract', 'rule', 'system_prompt'];\n private const TYPE_LABELS = [\n 'author_profile' => 'Autorenprofil',\n 'structure' => 'Struktur',\n 'organization' => 'Organisation',\n 'contract' => 'Contract',\n 'rule' => 'Regel',\n 'system_prompt' => 'System-Prompt',\n ];\n\n public function __construct()\n {\n $this->db = DatabaseFactory::content();\n }\n\n \/**\n * GET \/config\n *\/\n public function index(): void\n {\n $typeFilter = $this->getString('type');\n $statusFilter = $this->getString('status');\n\n $sql = 'SELECT c.*, p.name as parent_name\n FROM content_config c\n LEFT JOIN content_config p ON c.parent_id = p.id\n WHERE 1=1';\n $params = [];\n\n if ($typeFilter !== '' && in_array($typeFilter, self::TYPES, true)) {\n $sql .= ' AND c.type = ?';\n $params[] = $typeFilter;\n }\n\n if ($statusFilter !== '' && in_array($statusFilter, ['draft', 'active', 'deprecated'], true)) {\n $sql .= ' AND c.status = ?';\n $params[] = $statusFilter;\n }\n\n $sql .= ' ORDER BY c.type, c.name';\n\n $stmt = $this->db->prepare($sql);\n $stmt->execute($params);\n $configs = $stmt->fetchAll(\\PDO::FETCH_ASSOC);\n\n $stats = $this->getStatistics();\n\n $this->view('config.index', [\n 'title' => 'Content-Konfiguration',\n 'configs' => $configs,\n 'stats' => $stats,\n 'types' => self::TYPES,\n 'typeLabels' => self::TYPE_LABELS,\n 'currentType' => $typeFilter,\n 'currentStatus' => $statusFilter,\n ]);\n }\n\n \/**\n * GET \/config\/new\n *\/\n public function configNew(): void\n {\n $parents = $this->getParentOptions();\n\n $this->view('config.form', [\n 'title' => 'Neue Konfiguration',\n 'config' => null,\n 'types' => self::TYPES,\n 'typeLabels' => self::TYPE_LABELS,\n 'parents' => $parents,\n 'isEdit' => false,\n ]);\n }\n\n \/**\n * POST \/config\n *\/\n public function store(): void\n {\n $this->requireCsrf();\n\n $type = $_POST['type'] ?? '';\n $name = trim($_POST['name'] ?? '');\n $slug = trim($_POST['slug'] ?? '');\n $description = trim($_POST['description'] ?? '');\n $content = $_POST['content'] ?? '{}';\n $version = trim($_POST['version'] ?? '1.0');\n $status = $_POST['status'] ?? 'draft';\n $parentId = !empty($_POST['parent_id']) ? (int) $_POST['parent_id'] : null;\n\n if (!in_array($type, self::TYPES, true) || $name === '' || $slug === '') {\n $_SESSION['error'] = 'Typ, Name und Slug sind erforderlich.';\n header('Location: \/config\/new');\n exit;\n }\n\n \/\/ JSON validieren\n $decoded = json_decode($content, true);\n if ($decoded === null && $content !== 'null') {\n $_SESSION['error'] = 'Ungültiges JSON-Format.';\n header('Location: \/config\/new');\n exit;\n }\n\n $stmt = $this->db->prepare(\n 'INSERT INTO content_config (type, name, slug, description, content, version, status, parent_id)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)'\n );\n $stmt->execute([$type, $name, $slug, $description ?: null, $content, $version, $status, $parentId]);\n $id = $this->db->lastInsertId();\n\n $_SESSION['success'] = 'Konfiguration erfolgreich erstellt.';\n header('Location: \/config\/' . $id);\n exit;\n }\n\n \/**\n * GET \/config\/{id}\n *\/\n public function show(string $id): void\n {\n $config = $this->findById((int) $id);\n\n if ($config === null) {\n $this->notFound('Konfiguration nicht gefunden');\n }\n\n $children = $this->getChildren((int) $id);\n $history = $this->getHistory((int) $id);\n\n $this->view('config.show', [\n 'title' => $config['name'],\n 'config' => $config,\n 'children' => $children,\n 'history' => $history,\n 'typeLabels' => self::TYPE_LABELS,\n ]);\n }\n\n \/**\n * GET \/config\/{id}\/edit\n *\/\n public function edit(string $id): void\n {\n $config = $this->findById((int) $id);\n\n if ($config === null) {\n $this->notFound('Konfiguration nicht gefunden');\n }\n\n $parents = $this->getParentOptions((int) $id);\n\n $this->view('config.form', [\n 'title' => 'Bearbeiten: ' . $config['name'],\n 'config' => $config,\n 'types' => self::TYPES,\n 'typeLabels' => self::TYPE_LABELS,\n 'parents' => $parents,\n 'isEdit' => true,\n ]);\n }\n\n \/**\n * POST \/config\/{id}\n *\/\n public function update(string $id): void\n {\n $this->requireCsrf();\n\n $config = $this->findById((int) $id);\n\n if ($config === null) {\n $this->notFound('Konfiguration nicht gefunden');\n }\n\n $name = trim($_POST['name'] ?? '');\n $slug = trim($_POST['slug'] ?? '');\n $description = trim($_POST['description'] ?? '');\n $content = $_POST['content'] ?? '{}';\n $newVersion = trim($_POST['new_version'] ?? '');\n $changeDescription = trim($_POST['change_description'] ?? '');\n $status = $_POST['status'] ?? $config['status'];\n $parentId = !empty($_POST['parent_id']) ? (int) $_POST['parent_id'] : null;\n\n if ($name === '' || $slug === '' || $newVersion === '') {\n $_SESSION['error'] = 'Name, Slug und neue Version sind erforderlich.';\n header('Location: \/config\/' . $id . '\/edit');\n exit;\n }\n\n \/\/ JSON validieren\n $decoded = json_decode($content, true);\n if ($decoded === null && $content !== 'null') {\n $_SESSION['error'] = 'Ungültiges JSON-Format.';\n header('Location: \/config\/' . $id . '\/edit');\n exit;\n }\n\n \/\/ Alte Version in History speichern\n $stmt = $this->db->prepare(\n 'INSERT INTO content_config_history (config_id, content, version, changed_by, change_description)\n VALUES (?, ?, ?, ?, ?)'\n );\n $stmt->execute([\n $id,\n $config['content'],\n $config['version'],\n 'web-ui',\n $changeDescription ?: 'Update auf Version ' . $newVersion,\n ]);\n\n \/\/ Config aktualisieren\n $stmt = $this->db->prepare(\n 'UPDATE content_config\n SET name = ?, slug = ?, description = ?, content = ?, version = ?, status = ?, parent_id = ?\n WHERE id = ?'\n );\n $stmt->execute([$name, $slug, $description ?: null, $content, $newVersion, $status, $parentId, $id]);\n\n $_SESSION['success'] = 'Konfiguration auf Version ' . $newVersion . ' aktualisiert.';\n header('Location: \/config\/' . $id);\n exit;\n }\n\n \/**\n * POST \/config\/{id}\/delete\n *\/\n public function delete(string $id): void\n {\n $this->requireCsrf();\n\n $config = $this->findById((int) $id);\n\n if ($config === null) {\n $this->notFound('Konfiguration nicht gefunden');\n }\n\n \/\/ Prüfen ob Children existieren\n $stmt = $this->db->prepare('SELECT COUNT(*) FROM content_config WHERE parent_id = ?');\n $stmt->execute([$id]);\n if ($stmt->fetchColumn() > 0) {\n $_SESSION['error'] = 'Kann nicht gelöscht werden: Es existieren verknüpfte Einträge.';\n header('Location: \/config\/' . $id);\n exit;\n }\n\n \/\/ History löschen\n $stmt = $this->db->prepare('DELETE FROM content_config_history WHERE config_id = ?');\n $stmt->execute([$id]);\n\n \/\/ Config löschen\n $stmt = $this->db->prepare('DELETE FROM content_config WHERE id = ?');\n $stmt->execute([$id]);\n\n $_SESSION['success'] = 'Konfiguration gelöscht.';\n header('Location: \/config');\n exit;\n }\n\n \/\/ ========== Private Helpers ==========\n\n private function findById(int $id): ?array\n {\n $stmt = $this->db->prepare(\n 'SELECT c.*, p.name as parent_name\n FROM content_config c\n LEFT JOIN content_config p ON c.parent_id = p.id\n WHERE c.id = ?'\n );\n $stmt->execute([$id]);\n $result = $stmt->fetch(\\PDO::FETCH_ASSOC);\n\n return $result !== false ? $result : null;\n }\n\n private function getStatistics(): array\n {\n $stats = ['total' => 0, 'by_type' => [], 'by_status' => []];\n\n $stmt = $this->db->query('SELECT type, COUNT(*) as cnt FROM content_config GROUP BY type');\n foreach ($stmt->fetchAll(\\PDO::FETCH_ASSOC) as $row) {\n $stats['by_type'][$row['type']] = (int) $row['cnt'];\n $stats['total'] += (int) $row['cnt'];\n }\n\n $stmt = $this->db->query('SELECT status, COUNT(*) as cnt FROM content_config GROUP BY status');\n foreach ($stmt->fetchAll(\\PDO::FETCH_ASSOC) as $row) {\n $stats['by_status'][$row['status']] = (int) $row['cnt'];\n }\n\n return $stats;\n }\n\n private function getParentOptions(?int $excludeId = null): array\n {\n $sql = 'SELECT id, type, name FROM content_config WHERE type IN (\"contract\", \"organization\") ORDER BY type, name';\n $stmt = $this->db->query($sql);\n $parents = [];\n foreach ($stmt->fetchAll(\\PDO::FETCH_ASSOC) as $row) {\n if ($excludeId !== null && (int) $row['id'] === $excludeId) {\n continue;\n }\n $parents[] = $row;\n }\n\n return $parents;\n }\n\n private... [TRUNCATED-89fcc95c0bb6b31d]",
"new_string": "<?php\n\nnamespace Controller;\n\nuse Framework\\Controller;\nuse Infrastructure\\Persistence\\ContentConfigRepository;\n\nclass ConfigController extends Controller\n{\n private ContentConfigRepository $configRepo;\n\n private const TYPES = ['author_profile', 'structure', 'organization', 'contract', 'rule', 'system_prompt'];\n private const TYPE_LABELS = [\n 'author_profile' => 'Autorenprofil',\n 'structure' => 'Struktur',\n 'organization' => 'Organisation',\n 'contract' => 'Contract',\n 'rule' => 'Regel',\n 'system_prompt' => 'System-Prompt',\n ];\n\n public function __construct(?ContentConfigRepository $configRepo = null)\n {\n $this->configRepo = $configRepo ?? new ContentConfigRepository();\n }\n\n public function index(): void\n {\n $typeFilter = $this->getString('type');\n $statusFilter = $this->getString('status');\n\n $validType = in_array($typeFilter, self::TYPES, true) ? $typeFilter : null;\n $validStatus = in_array($statusFilter, ['draft', 'active', 'deprecated'], true) ? $statusFilter : null;\n\n $this->view('config.index', [\n 'title' => 'Content-Konfiguration',\n 'configs' => $this->configRepo->findAll($validType, $validStatus),\n 'stats' => $this->configRepo->getStatistics(),\n 'types' => self::TYPES,\n 'typeLabels' => self::TYPE_LABELS,\n 'currentType' => $typeFilter,\n 'currentStatus' => $statusFilter,\n ]);\n }\n\n public function configNew(): void\n {\n $this->view('config.form', [\n 'title' => 'Neue Konfiguration',\n 'config' => null,\n 'types' => self::TYPES,\n 'typeLabels' => self::TYPE_LABELS,\n 'parents' => $this->configRepo->getParentOptions(),\n 'isEdit' => false,\n ]);\n }\n\n public function store(): void\n {\n $this->requireCsrf();\n\n $type = $_POST['type'] ?? '';\n $name = trim($_POST['name'] ?? '');\n $slug = trim($_POST['slug'] ?? '');\n $description = trim($_POST['description'] ?? '');\n $content = $_POST['content'] ?? '{}';\n $version = trim($_POST['version'] ?? '1.0');\n $status = $_POST['status'] ?? 'draft';\n $parentId = !empty($_POST['parent_id']) ? (int) $_POST['parent_id'] : null;\n\n if (!in_array($type, self::TYPES, true) || $name === '' || $slug === '') {\n $_SESSION['error'] = 'Typ, Name und Slug sind erforderlich.';\n header('Location: \/config\/new');\n exit;\n }\n\n $decoded = json_decode($content, true);\n if ($decoded === null && $content !== 'null') {\n $_SESSION['error'] = 'Ungültiges JSON-Format.';\n header('Location: \/config\/new');\n exit;\n }\n\n $id = $this->configRepo->create(\n $type,\n $name,\n $slug,\n $description !== '' ? $description : null,\n $content,\n $version,\n $status,\n $parentId\n );\n\n $_SESSION['success'] = 'Konfiguration erfolgreich erstellt.';\n header('Location: \/config\/' . $id);\n exit;\n }\n\n public function show(string $id): void\n {\n $config = $this->configRepo->findById((int) $id);\n if ($config === null) {\n $this->notFound('Konfiguration nicht gefunden');\n }\n\n $this->view('config.show', [\n 'title' => $config['name'],\n 'config' => $config,\n 'children' => $this->configRepo->getChildren((int) $id),\n 'history' => $this->configRepo->getHistory((int) $id),\n 'typeLabels' => self::TYPE_LABELS,\n ]);\n }\n\n public function edit(string $id): void\n {\n $config = $this->configRepo->findById((int) $id);\n if ($config === null) {\n $this->notFound('Konfiguration nicht gefunden');\n }\n\n $this->view('config.form', [\n 'title' => 'Bearbeiten: ' . $config['name'],\n 'config' => $config,\n 'types' => self::TYPES,\n 'typeLabels' => self::TYPE_LABELS,\n 'parents' => $this->configRepo->getParentOptions((int) $id),\n 'isEdit' => true,\n ]);\n }\n\n public function update(string $id): void\n {\n $this->requireCsrf();\n\n $config = $this->configRepo->findById((int) $id);\n if ($config === null) {\n $this->notFound('Konfiguration nicht gefunden');\n }\n\n $name = trim($_POST['name'] ?? '');\n $slug = trim($_POST['slug'] ?? '');\n $description = trim($_POST['description'] ?? '');\n $content = $_POST['content'] ?? '{}';\n $newVersion = trim($_POST['new_version'] ?? '');\n $changeDescription = trim($_POST['change_description'] ?? '');\n $status = $_POST['status'] ?? $config['status'];\n $parentId = !empty($_POST['parent_id']) ? (int) $_POST['parent_id'] : null;\n\n if ($name === '' || $slug === '' || $newVersion === '') {\n $_SESSION['error'] = 'Name, Slug und neue Version sind erforderlich.';\n header('Location: \/config\/' . $id . '\/edit');\n exit;\n }\n\n $decoded = json_decode($content, true);\n if ($decoded === null && $content !== 'null') {\n $_SESSION['error'] = 'Ungültiges JSON-Format.';\n header('Location: \/config\/' . $id . '\/edit');\n exit;\n }\n\n $this->configRepo->saveHistory(\n (int) $id,\n $config['content'],\n $config['version'],\n 'web-ui',\n $changeDescription !== '' ? $changeDescription : 'Update auf Version ' . $newVersion\n );\n\n $this->configRepo->update(\n (int) $id,\n $name,\n $slug,\n $description !== '' ? $description : null,\n $content,\n $newVersion,\n $status,\n $parentId\n );\n\n $_SESSION['success'] = 'Konfiguration auf Version ' . $newVersion . ' aktualisiert.';\n header('Location: \/config\/' . $id);\n exit;\n }\n\n public function delete(string $id): void\n {\n $this->requireCsrf();\n\n $config = $this->configRepo->findById((int) $id);\n if ($config === null) {\n $this->notFound('Konfiguration nicht gefunden');\n }\n\n if ($this->configRepo->countChildren((int) $id) > 0) {\n $_SESSION['error'] = 'Kann nicht gelöscht werden: Es existieren verknüpfte Einträge.';\n header('Location: \/config\/' . $id);\n exit;\n }\n\n $this->configRepo->deleteHistory((int) $id);\n $this->configRepo->delete((int) $id);\n\n $_SESSION['success'] = 'Konfiguration gelöscht.';\n header('Location: \/config');\n exit;\n }\n}"
}
}
{
"tool_response": {
"filePath": "\/var\/www\/dev.campus.systemische-tools.de\/src\/Controller\/ConfigController.php",
"oldString": "<?php\n\nnamespace Controller;\n\nuse Framework\\Controller;\nuse Infrastructure\\Config\\DatabaseFactory;\n\nclass ConfigController extends Controller\n{\n private \\PDO $db;\n\n private const TYPES = ['author_profile', 'structure', 'organization', 'contract', 'rule', 'system_prompt'];\n private const TYPE_LABELS = [\n 'author_profile' => 'Autorenprofil',\n 'structure' => 'Struktur',\n 'organization' => 'Organisation',\n 'contract' => 'Contract',\n 'rule' => 'Regel',\n 'system_prompt' => 'System-Prompt',\n ];\n\n public function __construct()\n {\n $this->db = DatabaseFactory::content();\n }\n\n \/**\n * GET \/config\n *\/\n public function index(): void\n {\n $typeFilter = $this->getString('type');\n $statusFilter = $this->getString('status');\n\n $sql = 'SELECT c.*, p.name as parent_name\n FROM content_config c\n LEFT JOIN content_config p ON c.parent_id = p.id\n WHERE 1=1';\n $params = [];\n\n if ($typeFilter !== '' && in_array($typeFilter, self::TYPES, true)) {\n $sql .= ' AND c.type = ?';\n $params[] = $typeFilter;\n }\n\n if ($statusFilter !== '' && in_array($statusFilter, ['draft', 'active', 'deprecated'], true)) {\n $sql .= ' AND c.status = ?';\n $params[] = $statusFilter;\n }\n\n $sql .= ' ORDER BY c.type, c.name';\n\n $stmt = $this->db->prepare($sql);\n $stmt->execute($params);\n $configs = $stmt->fetchAll(\\PDO::FETCH_ASSOC);\n\n $stats = $this->getStatistics();\n\n $this->view('config.index', [\n 'title' => 'Content-Konfiguration',\n 'configs' => $configs,\n 'stats' => $stats,\n 'types' => self::TYPES,\n 'typeLabels' => self::TYPE_LABELS,\n 'currentType' => $typeFilter,\n 'currentStatus' => $statusFilter,\n ]);\n }\n\n \/**\n * GET \/config\/new\n *\/\n public function configNew(): void\n {\n $parents = $this->getParentOptions();\n\n $this->view('config.form', [\n 'title' => 'Neue Konfiguration',\n 'config' => null,\n 'types' => self::TYPES,\n 'typeLabels' => self::TYPE_LABELS,\n 'parents' => $parents,\n 'isEdit' => false,\n ]);\n }\n\n \/**\n * POST \/config\n *\/\n public function store(): void\n {\n $this->requireCsrf();\n\n $type = $_POST['type'] ?? '';\n $name = trim($_POST['name'] ?? '');\n $slug = trim($_POST['slug'] ?? '');\n $description = trim($_POST['description'] ?? '');\n $content = $_POST['content'] ?? '{}';\n $version = trim($_POST['version'] ?? '1.0');\n $status = $_POST['status'] ?? 'draft';\n $parentId = !empty($_POST['parent_id']) ? (int) $_POST['parent_id'] : null;\n\n if (!in_array($type, self::TYPES, true) || $name === '' || $slug === '') {\n $_SESSION['error'] = 'Typ, Name und Slug sind erforderlich.';\n header('Location: \/config\/new');\n exit;\n }\n\n \/\/ JSON validieren\n $decoded = json_decode($content, true);\n if ($decoded === null && $content !== 'null') {\n $_SESSION['error'] = 'Ungültiges JSON-Format.';\n header('Location: \/config\/new');\n exit;\n }\n\n $stmt = $this->db->prepare(\n 'INSERT INTO content_config (type, name, slug, description, content, version, status, parent_id)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)'\n );\n $stmt->execute([$type, $name, $slug, $description ?: null, $content, $version, $status, $parentId]);\n $id = $this->db->lastInsertId();\n\n $_SESSION['success'] = 'Konfiguration erfolgreich erstellt.';\n header('Location: \/config\/' . $id);\n exit;\n }\n\n \/**\n * GET \/config\/{id}\n *\/\n public function show(string $id): void\n {\n $config = $this->findById((int) $id);\n\n if ($config === null) {\n $this->notFound('Konfiguration nicht gefunden');\n }\n\n $children = $this->getChildren((int) $id);\n $history = $this->getHistory((int) $id);\n\n $this->view('config.show', [\n 'title' => $config['name'],\n 'config' => $config,\n 'children' => $children,\n 'history' => $history,\n 'typeLabels' => self::TYPE_LABELS,\n ]);\n }\n\n \/**\n * GET \/config\/{id}\/edit\n *\/\n public function edit(string $id): void\n {\n $config = $this->findById((int) $id);\n\n if ($config === null) {\n $this->notFound('Konfiguration nicht gefunden');\n }\n\n $parents = $this->getParentOptions((int) $id);\n\n $this->view('config.form', [\n 'title' => 'Bearbeiten: ' . $config['name'],\n 'config' => $config,\n 'types' => self::TYPES,\n 'typeLabels' => self::TYPE_LABELS,\n 'parents' => $parents,\n 'isEdit' => true,\n ]);\n }\n\n \/**\n * POST \/config\/{id}\n *\/\n public function update(string $id): void\n {\n $this->requireCsrf();\n\n $config = $this->findById((int) $id);\n\n if ($config === null) {\n $this->notFound('Konfiguration nicht gefunden');\n }\n\n $name = trim($_POST['name'] ?? '');\n $slug = trim($_POST['slug'] ?? '');\n $description = trim($_POST['description'] ?? '');\n $content = $_POST['content'] ?? '{}';\n $newVersion = trim($_POST['new_version'] ?? '');\n $changeDescription = trim($_POST['change_description'] ?? '');\n $status = $_POST['status'] ?? $config['status'];\n $parentId = !empty($_POST['parent_id']) ? (int) $_POST['parent_id'] : null;\n\n if ($name === '' || $slug === '' || $newVersion === '') {\n $_SESSION['error'] = 'Name, Slug und neue Version sind erforderlich.';\n header('Location: \/config\/' . $id . '\/edit');\n exit;\n }\n\n \/\/ JSON validieren\n $decoded = json_decode($content, true);\n if ($decoded === null && $content !== 'null') {\n $_SESSION['error'] = 'Ungültiges JSON-Format.';\n header('Location: \/config\/' . $id . '\/edit');\n exit;\n }\n\n \/\/ Alte Version in History speichern\n $stmt = $this->db->prepare(\n 'INSERT INTO content_config_history (config_id, content, version, changed_by, change_description)\n VALUES (?, ?, ?, ?, ?)'\n );\n $stmt->execute([\n $id,\n $config['content'],\n $config['version'],\n 'web-ui',\n $changeDescription ?: 'Update auf Version ' . $newVersion,\n ]);\n\n \/\/ Config aktualisieren\n $stmt = $this->db->prepare(\n 'UPDATE content_config\n SET name = ?, slug = ?, description = ?, content = ?, version = ?, status = ?, parent_id = ?\n WHERE id = ?'\n );\n $stmt->execute([$name, $slug, $description ?: null, $content, $newVersion, $status, $parentId, $id]);\n\n $_SESSION['success'] = 'Konfiguration auf Version ' . $newVersion . ' aktualisiert.';\n header('Location: \/config\/' . $id);\n exit;\n }\n\n \/**\n * POST \/config\/{id}\/delete\n *\/\n public function delete(string $id): void\n {\n $this->requireCsrf();\n\n $config = $this->findById((int) $id);\n\n if ($config === null) {\n $this->notFound('Konfiguration nicht gefunden');\n }\n\n \/\/ Prüfen ob Children existieren\n $stmt = $this->db->prepare('SELECT COUNT(*) FROM content_config WHERE parent_id = ?');\n $stmt->execute([$id]);\n if ($stmt->fetchColumn() > 0) {\n $_SESSION['error'] = 'Kann nicht gelöscht werden: Es existieren verknüpfte Einträge.';\n header('Location: \/config\/' . $id);\n exit;\n }\n\n \/\/ History löschen\n $stmt = $this->db->prepare('DELETE FROM content_config_history WHERE config_id = ?');\n $stmt->execute([$id]);\n\n \/\/ Config löschen\n $stmt = $this->db->prepare('DELETE FROM content_config WHERE id = ?');\n $stmt->execute([$id]);\n\n $_SESSION['success'] = 'Konfiguration gelöscht.';\n header('Location: \/config');\n exit;\n }\n\n \/\/ ========== Private Helpers ==========\n\n private function findById(int $id): ?array\n {\n $stmt = $this->db->prepare(\n 'SELECT c.*, p.name as parent_name\n FROM content_config c\n LEFT JOIN content_config p ON c.parent_id = p.id\n WHERE c.id = ?'\n );\n $stmt->execute([$id]);\n $result = $stmt->fetch(\\PDO::FETCH_ASSOC);\n\n return $result !== false ? $result : null;\n }\n\n private function getStatistics(): array\n {\n $stats = ['total' => 0, 'by_type' => [], 'by_status' => []];\n\n $stmt = $this->db->query('SELECT type, COUNT(*) as cnt FROM content_config GROUP BY type');\n foreach ($stmt->fetchAll(\\PDO::FETCH_ASSOC) as $row) {\n $stats['by_type'][$row['type']] = (int) $row['cnt'];\n $stats['total'] += (int) $row['cnt'];\n }\n\n $stmt = $this->db->query('SELECT status, COUNT(*) as cnt FROM content_config GROUP BY status');\n foreach ($stmt->fetchAll(\\PDO::FETCH_ASSOC) as $row) {\n $stats['by_status'][$row['status']] = (int) $row['cnt'];\n }\n\n return $stats;\n }\n\n private function getParentOptions(?int $excludeId = null): array\n {\n $sql = 'SELECT id, type, name FROM content_config WHERE type IN (\"contract\", \"organization\") ORDER BY type, name';\n $stmt = $this->db->query($sql);\n $parents = [];\n foreach ($stmt->fetchAll(\\PDO::FETCH_ASSOC) as $row) {\n if ($excludeId !== null && (int) $row['id'] === $excludeId) {\n continue;\n }\n $parents[] = $row;\n }\n\n return $parents;\n }\n\n private... [TRUNCATED-89fcc95c0bb6b31d]",
"newString": "<?php\n\nnamespace Controller;\n\nuse Framework\\Controller;\nuse Infrastructure\\Persistence\\ContentConfigRepository;\n\nclass ConfigController extends Controller\n{\n private ContentConfigRepository $configRepo;\n\n private const TYPES = ['author_profile', 'structure', 'organization', 'contract', 'rule', 'system_prompt'];\n private const TYPE_LABELS = [\n 'author_profile' => 'Autorenprofil',\n 'structure' => 'Struktur',\n 'organization' => 'Organisation',\n 'contract' => 'Contract',\n 'rule' => 'Regel',\n 'system_prompt' => 'System-Prompt',\n ];\n\n public function __construct(?ContentConfigRepository $configRepo = null)\n {\n $this->configRepo = $configRepo ?? new ContentConfigRepository();\n }\n\n public function index(): void\n {\n $typeFilter = $this->getString('type');\n $statusFilter = $this->getString('status');\n\n $validType = in_array($typeFilter, self::TYPES, true) ? $typeFilter : null;\n $validStatus = in_array($statusFilter, ['draft', 'active', 'deprecated'], true) ? $statusFilter : null;\n\n $this->view('config.index', [\n 'title' => 'Content-Konfiguration',\n 'configs' => $this->configRepo->findAll($validType, $validStatus),\n 'stats' => $this->configRepo->getStatistics(),\n 'types' => self::TYPES,\n 'typeLabels' => self::TYPE_LABELS,\n 'currentType' => $typeFilter,\n 'currentStatus' => $statusFilter,\n ]);\n }\n\n public function configNew(): void\n {\n $this->view('config.form', [\n 'title' => 'Neue Konfiguration',\n 'config' => null,\n 'types' => self::TYPES,\n 'typeLabels' => self::TYPE_LABELS,\n 'parents' => $this->configRepo->getParentOptions(),\n 'isEdit' => false,\n ]);\n }\n\n public function store(): void\n {\n $this->requireCsrf();\n\n $type = $_POST['type'] ?? '';\n $name = trim($_POST['name'] ?? '');\n $slug = trim($_POST['slug'] ?? '');\n $description = trim($_POST['description'] ?? '');\n $content = $_POST['content'] ?? '{}';\n $version = trim($_POST['version'] ?? '1.0');\n $status = $_POST['status'] ?? 'draft';\n $parentId = !empty($_POST['parent_id']) ? (int) $_POST['parent_id'] : null;\n\n if (!in_array($type, self::TYPES, true) || $name === '' || $slug === '') {\n $_SESSION['error'] = 'Typ, Name und Slug sind erforderlich.';\n header('Location: \/config\/new');\n exit;\n }\n\n $decoded = json_decode($content, true);\n if ($decoded === null && $content !== 'null') {\n $_SESSION['error'] = 'Ungültiges JSON-Format.';\n header('Location: \/config\/new');\n exit;\n }\n\n $id = $this->configRepo->create(\n $type,\n $name,\n $slug,\n $description !== '' ? $description : null,\n $content,\n $version,\n $status,\n $parentId\n );\n\n $_SESSION['success'] = 'Konfiguration erfolgreich erstellt.';\n header('Location: \/config\/' . $id);\n exit;\n }\n\n public function show(string $id): void\n {\n $config = $this->configRepo->findById((int) $id);\n if ($config === null) {\n $this->notFound('Konfiguration nicht gefunden');\n }\n\n $this->view('config.show', [\n 'title' => $config['name'],\n 'config' => $config,\n 'children' => $this->configRepo->getChildren((int) $id),\n 'history' => $this->configRepo->getHistory((int) $id),\n 'typeLabels' => self::TYPE_LABELS,\n ]);\n }\n\n public function edit(string $id): void\n {\n $config = $this->configRepo->findById((int) $id);\n if ($config === null) {\n $this->notFound('Konfiguration nicht gefunden');\n }\n\n $this->view('config.form', [\n 'title' => 'Bearbeiten: ' . $config['name'],\n 'config' => $config,\n 'types' => self::TYPES,\n 'typeLabels' => self::TYPE_LABELS,\n 'parents' => $this->configRepo->getParentOptions((int) $id),\n 'isEdit' => true,\n ]);\n }\n\n public function update(string $id): void\n {\n $this->requireCsrf();\n\n $config = $this->configRepo->findById((int) $id);\n if ($config === null) {\n $this->notFound('Konfiguration nicht gefunden');\n }\n\n $name = trim($_POST['name'] ?? '');\n $slug = trim($_POST['slug'] ?? '');\n $description = trim($_POST['description'] ?? '');\n $content = $_POST['content'] ?? '{}';\n $newVersion = trim($_POST['new_version'] ?? '');\n $changeDescription = trim($_POST['change_description'] ?? '');\n $status = $_POST['status'] ?? $config['status'];\n $parentId = !empty($_POST['parent_id']) ? (int) $_POST['parent_id'] : null;\n\n if ($name === '' || $slug === '' || $newVersion === '') {\n $_SESSION['error'] = 'Name, Slug und neue Version sind erforderlich.';\n header('Location: \/config\/' . $id . '\/edit');\n exit;\n }\n\n $decoded = json_decode($content, true);\n if ($decoded === null && $content !== 'null') {\n $_SESSION['error'] = 'Ungültiges JSON-Format.';\n header('Location: \/config\/' . $id . '\/edit');\n exit;\n }\n\n $this->configRepo->saveHistory(\n (int) $id,\n $config['content'],\n $config['version'],\n 'web-ui',\n $changeDescription !== '' ? $changeDescription : 'Update auf Version ' . $newVersion\n );\n\n $this->configRepo->update(\n (int) $id,\n $name,\n $slug,\n $description !== '' ? $description : null,\n $content,\n $newVersion,\n $status,\n $parentId\n );\n\n $_SESSION['success'] = 'Konfiguration auf Version ' . $newVersion . ' aktualisiert.';\n header('Location: \/config\/' . $id);\n exit;\n }\n\n public function delete(string $id): void\n {\n $this->requireCsrf();\n\n $config = $this->configRepo->findById((int) $id);\n if ($config === null) {\n $this->notFound('Konfiguration nicht gefunden');\n }\n\n if ($this->configRepo->countChildren((int) $id) > 0) {\n $_SESSION['error'] = 'Kann nicht gelöscht werden: Es existieren verknüpfte Einträge.';\n header('Location: \/config\/' . $id);\n exit;\n }\n\n $this->configRepo->deleteHistory((int) $id);\n $this->configRepo->delete((int) $id);\n\n $_SESSION['success'] = 'Konfiguration gelöscht.';\n header('Location: \/config');\n exit;\n }\n}",
"originalFile": "<?php\n\nnamespace Controller;\n\nuse Framework\\Controller;\nuse Infrastructure\\Config\\DatabaseFactory;\n\nclass ConfigController extends Controller\n{\n private \\PDO $db;\n\n private const TYPES = ['author_profile', 'structure', 'organization', 'contract', 'rule', 'system_prompt'];\n private const TYPE_LABELS = [\n 'author_profile' => 'Autorenprofil',\n 'structure' => 'Struktur',\n 'organization' => 'Organisation',\n 'contract' => 'Contract',\n 'rule' => 'Regel',\n 'system_prompt' => 'System-Prompt',\n ];\n\n public function __construct()\n {\n $this->db = DatabaseFactory::content();\n }\n\n \/**\n * GET \/config\n *\/\n public function index(): void\n {\n $typeFilter = $this->getString('type');\n $statusFilter = $this->getString('status');\n\n $sql = 'SELECT c.*, p.name as parent_name\n FROM content_config c\n LEFT JOIN content_config p ON c.parent_id = p.id\n WHERE 1=1';\n $params = [];\n\n if ($typeFilter !== '' && in_array($typeFilter, self::TYPES, true)) {\n $sql .= ' AND c.type = ?';\n $params[] = $typeFilter;\n }\n\n if ($statusFilter !== '' && in_array($statusFilter, ['draft', 'active', 'deprecated'], true)) {\n $sql .= ' AND c.status = ?';\n $params[] = $statusFilter;\n }\n\n $sql .= ' ORDER BY c.type, c.name';\n\n $stmt = $this->db->prepare($sql);\n $stmt->execute($params);\n $configs = $stmt->fetchAll(\\PDO::FETCH_ASSOC);\n\n $stats = $this->getStatistics();\n\n $this->view('config.index', [\n 'title' => 'Content-Konfiguration',\n 'configs' => $configs,\n 'stats' => $stats,\n 'types' => self::TYPES,\n 'typeLabels' => self::TYPE_LABELS,\n 'currentType' => $typeFilter,\n 'currentStatus' => $statusFilter,\n ]);\n }\n\n \/**\n * GET \/config\/new\n *\/\n public function configNew(): void\n {\n $parents = $this->getParentOptions();\n\n $this->view('config.form', [\n 'title' => 'Neue Konfiguration',\n 'config' => null,\n 'types' => self::TYPES,\n 'typeLabels' => self::TYPE_LABELS,\n 'parents' => $parents,\n 'isEdit' => false,\n ]);\n }\n\n \/**\n * POST \/config\n *\/\n public function store(): void\n {\n $this->requireCsrf();\n\n $type = $_POST['type'] ?? '';\n $name = trim($_POST['name'] ?? '');\n $slug = trim($_POST['slug'] ?? '');\n $description = trim($_POST['description'] ?? '');\n $content = $_POST['content'] ?? '{}';\n $version = trim($_POST['version'] ?? '1.0');\n $status = $_POST['status'] ?? 'draft';\n $parentId = !empty($_POST['parent_id']) ? (int) $_POST['parent_id'] : null;\n\n if (!in_array($type, self::TYPES, true) || $name === '' || $slug === '') {\n $_SESSION['error'] = 'Typ, Name und Slug sind erforderlich.';\n header('Location: \/config\/new');\n exit;\n }\n\n \/\/ JSON validieren\n $decoded = json_decode($content, true);\n if ($decoded === null && $content !== 'null') {\n $_SESSION['error'] = 'Ungültiges JSON-Format.';\n header('Location: \/config\/new');\n exit;\n }\n\n $stmt = $this->db->prepare(\n 'INSERT INTO content_config (type, name, slug, description, content, version, status, parent_id)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)'\n );\n $stmt->execute([$type, $name, $slug, $description ?: null, $content, $version, $status, $parentId]);\n $id = $this->db->lastInsertId();\n\n $_SESSION['success'] = 'Konfiguration erfolgreich erstellt.';\n header('Location: \/config\/' . $id);\n exit;\n }\n\n \/**\n * GET \/config\/{id}\n *\/\n public function show(string $id): void\n {\n $config = $this->findById((int) $id);\n\n if ($config === null) {\n $this->notFound('Konfiguration nicht gefunden');\n }\n\n $children = $this->getChildren((int) $id);\n $history = $this->getHistory((int) $id);\n\n $this->view('config.show', [\n 'title' => $config['name'],\n 'config' => $config,\n 'children' => $children,\n 'history' => $history,\n 'typeLabels' => self::TYPE_LABELS,\n ]);\n }\n\n \/**\n * GET \/config\/{id}\/edit\n *\/\n public function edit(string $id): void\n {\n $config = $this->findById((int) $id);\n\n if ($config === null) {\n $this->notFound('Konfiguration nicht gefunden');\n }\n\n $parents = $this->getParentOptions((int) $id);\n\n $this->view('config.form', [\n 'title' => 'Bearbeiten: ' . $config['name'],\n 'config' => $config,\n 'types' => self::TYPES,\n 'typeLabels' => self::TYPE_LABELS,\n 'parents' => $parents,\n 'isEdit' => true,\n ]);\n }\n\n \/**\n * POST \/config\/{id}\n *\/\n public function update(string $id): void\n {\n $this->requireCsrf();\n\n $config = $this->findById((int) $id);\n\n if ($config === null) {\n $this->notFound('Konfiguration nicht gefunden');\n }\n\n $name = trim($_POST['name'] ?? '');\n $slug = trim($_POST['slug'] ?? '');\n $description = trim($_POST['description'] ?? '');\n $content = $_POST['content'] ?? '{}';\n $newVersion = trim($_POST['new_version'] ?? '');\n $changeDescription = trim($_POST['change_description'] ?? '');\n $status = $_POST['status'] ?? $config['status'];\n $parentId = !empty($_POST['parent_id']) ? (int) $_POST['parent_id'] : null;\n\n if ($name === '' || $slug === '' || $newVersion === '') {\n $_SESSION['error'] = 'Name, Slug und neue Version sind erforderlich.';\n header('Location: \/config\/' . $id . '\/edit');\n exit;\n }\n\n \/\/ JSON validieren\n $decoded = json_decode($content, true);\n if ($decoded === null && $content !== 'null') {\n $_SESSION['error'] = 'Ungültiges JSON-Format.';\n header('Location: \/config\/' . $id . '\/edit');\n exit;\n }\n\n \/\/ Alte Version in History speichern\n $stmt = $this->db->prepare(\n 'INSERT INTO content_config_history (config_id, content, version, changed_by, change_description)\n VALUES (?, ?, ?, ?, ?)'\n );\n $stmt->execute([\n $id,\n $config['content'],\n $config['version'],\n 'web-ui',\n $changeDescription ?: 'Update auf Version ' . $newVersion,\n ]);\n\n \/\/ Config aktualisieren\n $stmt = $this->db->prepare(\n 'UPDATE content_config\n SET name = ?, slug = ?, description = ?, content = ?, version = ?, status = ?, parent_id = ?\n WHERE id = ?'\n );\n $stmt->execute([$name, $slug, $description ?: null, $content, $newVersion, $status, $parentId, $id]);\n\n $_SESSION['success'] = 'Konfiguration auf Version ' . $newVersion . ' aktualisiert.';\n header('Location: \/config\/' . $id);\n exit;\n }\n\n \/**\n * POST \/config\/{id}\/delete\n *\/\n public function delete(string $id): void\n {\n $this->requireCsrf();\n\n $config = $this->findById((int) $id);\n\n if ($config === null) {\n $this->notFound('Konfiguration nicht gefunden');\n }\n\n \/\/ Prüfen ob Children existieren\n $stmt = $this->db->prepare('SELECT COUNT(*) FROM content_config WHERE parent_id = ?');\n $stmt->execute([$id]);\n if ($stmt->fetchColumn() > 0) {\n $_SESSION['error'] = 'Kann nicht gelöscht werden: Es existieren verknüpfte Einträge.';\n header('Location: \/config\/' . $id);\n exit;\n }\n\n \/\/ History löschen\n $stmt = $this->db->prepare('DELETE FROM content_config_history WHERE config_id = ?');\n $stmt->execute([$id]);\n\n \/\/ Config löschen\n $stmt = $this->db->prepare('DELETE FROM content_config WHERE id = ?');\n $stmt->execute([$id]);\n\n $_SESSION['success'] = 'Konfiguration gelöscht.';\n header('Location: \/config');\n exit;\n }\n\n \/\/ ========== Private Helpers ==========\n\n private function findById(int $id): ?array\n {\n $stmt = $this->db->prepare(\n 'SELECT c.*, p.name as parent_name\n FROM content_config c\n LEFT JOIN content_config p ON c.parent_id = p.id\n WHERE c.id = ?'\n );\n $stmt->execute([$id]);\n $result = $stmt->fetch(\\PDO::FETCH_ASSOC);\n\n return $result !== false ? $result : null;\n }\n\n private function getStatistics(): array\n {\n $stats = ['total' => 0, 'by_type' => [], 'by_status' => []];\n\n $stmt = $this->db->query('SELECT type, COUNT(*) as cnt FROM content_config GROUP BY type');\n foreach ($stmt->fetchAll(\\PDO::FETCH_ASSOC) as $row) {\n $stats['by_type'][$row['type']] = (int) $row['cnt'];\n $stats['total'] += (int) $row['cnt'];\n }\n\n $stmt = $this->db->query('SELECT status, COUNT(*) as cnt FROM content_config GROUP BY status');\n foreach ($stmt->fetchAll(\\PDO::FETCH_ASSOC) as $row) {\n $stats['by_status'][$row['status']] = (int) $row['cnt'];\n }\n\n return $stats;\n }\n\n private function getParentOptions(?int $excludeId = null): array\n {\n $sql = 'SELECT id, type, name FROM content_config WHERE type IN (\"contract\", \"organization\") ORDER BY type, name';\n $stmt = $this->db->query($sql);\n $parents = [];\n foreach ($stmt->fetchAll(\\PDO::FETCH_ASSOC) as $row) {\n if ($excludeId !== null && (int) $row['id'] === $excludeId) {\n continue;\n }\n $parents[] = $row;\n }\n\n return $parents;\n }\n\n private... [TRUNCATED-672ae65975ec2580]",
"structuredPatch": [
{
"oldStart": 3,
"oldLines": 11,
"newStart": 3,
"newLines": 11,
"lines": [
" namespace Controller;",
" ",
" use Framework\\Controller;",
"-use Infrastructure\\Config\\DatabaseFactory;",
"+use Infrastructure\\Persistence\\ContentConfigRepository;",
" ",
" class ConfigController extends Controller",
" {",
"- private \\PDO $db;",
"+ private ContentConfigRepository $configRepo;",
" ",
" private const TYPES = ['author_profile', 'structure', 'organization', 'contract', 'rule', 'system_prompt'];",
" private const TYPE_LABELS = ["
]
},
{
"oldStart": 19,
"oldLines": 47,
"newStart": 19,
"newLines": 23,
"lines": [
" 'system_prompt' => 'System-Prompt',",
" ];",
" ",
"- public function __construct()",
"+ public function __construct(?ContentConfigRepository $configRepo = null)",
" {",
"- $this->db = DatabaseFactory::content();",
"+ $this->configRepo = $configRepo ?? new ContentConfigRepository();",
" }",
" ",
"- \/**",
"- * GET \/config",
"- *\/",
" public function index(): void",
" {",
" $typeFilter = $this->getString('type');",
" $statusFilter = $this->getString('status');",
" ",
"- $sql = 'SELECT c.*, p.name as parent_name",
"- FROM content_config c",
"- LEFT JOIN content_config p ON c.parent_id = p.id",
"- WHERE 1=1';",
"- $params = [];",
"+ $validType = in_array($typeFilter, self::TYPES, true) ? $typeFilter : null;",
"+ $validStatus = in_array($statusFilter, ['draft', 'active', 'deprecated'], true) ? $statusFilter : null;",
" ",
"- if ($typeFilter !== '' && in_array($typeFilter, self::TYPES, true)) {",
"- $sql .= ' AND c.type = ?';",
"- $params[] = $typeFilter;",
"- }",
"-",
"- if ($statusFilter !== '' && in_array($statusFilter, ['draft', 'active', 'deprecated'], true)) {",
"- $sql .= ' AND c.status = ?';",
"- $params[] = $statusFilter;",
"- }",
"-",
"- $sql .= ' ORDER BY c.type, c.name';",
"-",
"- $stmt = $this->db->prepare($sql);",
"- $stmt->execute($params);",
"- $configs = $stmt->fetchAll(\\PDO::FETCH_ASSOC);",
"-",
"- $stats = $this->getStatistics();",
"-",
" $this->view('config.index', [",
" 'title' => 'Content-Konfiguration',",
"- 'configs' => $configs,",
"- 'stats' => $stats,",
"+ 'configs' => $this->configRepo->findAll($validType, $validStatus),",
"+ 'stats' => $this->configRepo->getStatistics(),",
" 'types' => self::TYPES,",
" 'typeLabels' => self::TYPE_LABELS,",
" 'currentType' => $typeFilter,"
]
},
{
"oldStart": 67,
"oldLines": 26,
"newStart": 43,
"newLines": 18,
"lines": [
" ]);",
" }",
" ",
"- \/**",
"- * GET \/config\/new",
"- *\/",
" public function configNew(): void",
" {",
"- $parents = $this->getParentOptions();",
"-",
" $this->view('config.form', [",
" 'title' => 'Neue Konfiguration',",
" 'config' => null,",
" 'types' => self::TYPES,",
" 'typeLabels' => self::TYPE_LABELS,",
"- 'parents' => $parents,",
"+ 'parents' => $this->configRepo->getParentOptions(),",
" 'isEdit' => false,",
" ]);",
" }",
" ",
"- \/**",
"- * POST \/config",
"- *\/",
" public function store(): void",
" {",
" $this->requireCsrf();"
]
},
{
"oldStart": 106,
"oldLines": 7,
"newStart": 74,
"newLines": 6,
"lines": [
" exit;",
" }",
" ",
"- \/\/ JSON validieren",
" $decoded = json_decode($content, true);",
" if ($decoded === null && $content !== 'null') {",
" $_SESSION['error'] = 'Ungültiges JSON-Format.';"
]
},
{
"oldStart": 114,
"oldLines": 73,
"newStart": 81,
"newLines": 60,
"lines": [
" exit;",
" }",
" ",
"- $stmt = $this->db->prepare(",
"- 'INSERT INTO content_config (type, name, slug, description, content, version, status, parent_id)",
"- VALUES (?, ?, ?, ?, ?, ?, ?, ?)'",
"+ $id = $this->configRepo->create(",
"+ $type,",
"+ $name,",
"+ $slug,",
"+ $description !== '' ? $description : null,",
"+ $content,",
"+ $version,",
"+ $status,",
"+ $parentId",
" );",
"- $stmt->execute([$type, $name, $slug, $description ?: null, $content, $version, $status, $parentId]);",
"- $id = $this->db->lastInsertId();",
" ",
" $_SESSION['success'] = 'Konfiguration erfolgreich erstellt.';",
" header('Location: \/config\/' . $id);",
" exit;",
" }",
" ",
"- \/**",
"- * GET \/config\/{id}",
"- *\/",
" public function show(string $id): void",
" {",
"- $config = $this->findById((int) $id);",
"-",
"+ $config = $this->configRepo->findById((int) $id);",
" if ($config === null) {",
" $this->notFound('Konfiguration nicht gefunden');",
" }",
" ",
"- $children = $this->getChildren((int) $id);",
"- $history = $this->getHistory((int) $id);",
"-",
" $this->view('config.show', [",
" 'title' => $config['name'],",
" 'config' => $config,",
"- 'children' => $children,",
"- 'history' => $history,",
"+ 'children' => $this->configRepo->getChildren((int) $id),",
"+ 'history' => $this->configRepo->getHistory((int) $id),",
" 'typeLabels' => self::TYPE_LABELS,",
" ]);",
" }",
" ",
"- \/**",
"- * GET \/config\/{id}\/edit",
"- *\/",
" public function edit(string $id): void",
" {",
"- $config = $this->findById((int) $id);",
"-",
"+ $config = $this->configRepo->findById((int) $id);",
" if ($config === null) {",
" $this->notFound('Konfiguration nicht gefunden');",
" }",
" ",
"- $parents = $this->getParentOptions((int) $id);",
"-",
" $this->view('config.form', [",
" 'title' => 'Bearbeiten: ' . $config['name'],",
" 'config' => $config,",
" 'types' => self::TYPES,",
" 'typeLabels' => self::TYPE_LABELS,",
"- 'parents' => $parents,",
"+ 'parents' => $this->configRepo->getParentOptions((int) $id),",
" 'isEdit' => true,",
" ]);",
" }",
" ",
"- \/**",
"- * POST \/config\/{id}",
"- *\/",
" public function update(string $id): void",
" {",
" $this->requireCsrf();",
" ",
"- $config = $this->findById((int) $id);",
"-",
"+ $config = $this->configRepo->findById((int) $id);",
" if ($config === null) {",
" $this->notFound('Konfiguration nicht gefunden');",
" }"
]
},
{
"oldStart": 200,
"oldLines": 7,
"newStart": 154,
"newLines": 6,
"lines": [
" exit;",
" }",
" ",
"- \/\/ JSON validieren",
" $decoded = json_decode($content, true);",
" if ($decoded === null && $content !== 'null') {",
" $_SESSION['error'] = 'Ungültiges JSON-Format.';"
]
},
{
"oldStart": 208,
"oldLines": 131,
"newStart": 161,
"newLines": 50,
"lines": [
" exit;",
" }",
" ",
"- \/\/ Alte Version in History speichern",
"- $stmt = $this->db->prepare(",
"- 'INSERT INTO content_config_history (config_id, content, version, changed_by, change_description)",
"- VALUES (?, ?, ?, ?, ?)'",
"- );",
"- $stmt->execute([",
"- $id,",
"+ $this->configRepo->saveHistory(",
"+ (int) $id,",
" $config['content'],",
" $config['version'],",
" 'web-ui',",
"- $changeDescription ?: 'Update auf Version ' . $newVersion,",
"- ]);",
"+ $changeDescription !== '' ? $changeDescription : 'Update auf Version ' . $newVersion",
"+ );",
" ",
"- \/\/ Config aktualisieren",
"- $stmt = $this->db->prepare(",
"- 'UPDATE content_config",
"- SET name = ?, slug = ?, description = ?, content = ?, version = ?, status = ?, parent_id = ?",
"- WHERE id = ?'",
"+ $this->configRepo->update(",
"+ (int) $id,",
"+ $name,",
"+ $slug,",
"+ $description !== '' ? $description : null,",
"+ $content,",
"+ $newVersion,",
"+ $status,",
"+ $parentId",
" );",
"- $stmt->execute([$name, $slug, $description ?: null, $content, $newVersion, $status, $parentId, $id]);",
" ",
" $_SESSION['success'] = 'Konfiguration auf Version ' . $newVersion . ' aktualisiert.';",
" header('Location: \/config\/' . $id);",
" exit;",
" }",
" ",
"- \/**",
"- * POST \/config\/{id}\/delete",
"- *\/",
" public function delete(string $id): void",
" {",
" $this->requireCsrf();",
" ",
"- $config = $this->findById((int) $id);",
"-",
"+ $config = $this->configRepo->findById((int) $id);",
" if ($config === null) {",
" $this->notFound('Konfiguration nicht gefunden');",
" }",
" ",
"- \/\/ Prüfen ob Children existieren",
"- $stmt = $this->db->prepare('SELECT COUNT(*) FROM content_config WHERE parent_id = ?');",
"- $stmt->execute([$id]);",
"- if ($stmt->fetchColumn() > 0) {",
"+ if ($this->configRepo->countChildren((int) $id) > 0) {",
" $_SESSION['error'] = 'Kann nicht gelöscht werden: Es existieren verknüpfte Einträge.';",
" header('Location: \/config\/' . $id);",
" exit;",
" }",
" ",
"- \/\/ History löschen",
"- $stmt = $this->db->prepare('DELETE FROM content_config_history WHERE config_id = ?');",
"- $stmt->execute([$id]);",
"+ $this->configRepo->deleteHistory((int) $id);",
"+ $this->configRepo->delete((int) $id);",
" ",
"- \/\/ Config löschen",
"- $stmt = $this->db->prepare('DELETE FROM content_config WHERE id = ?');",
"- $stmt->execute([$id]);",
"-",
" $_SESSION['success'] = 'Konfiguration gelöscht.';",
" header('Location: \/config');",
" exit;",
" }",
"-",
"- \/\/ ========== Private Helpers ==========",
"-",
"- private function findById(int $id): ?array",
"- {",
"- $stmt = $this->db->prepare(",
"- 'SELECT c.*, p.name as parent_name",
"- FROM content_config c",
"- LEFT JOIN content_config p ON c.parent_id = p.id",
"- WHERE c.id = ?'",
"- );",
"- $stmt->execute([$id]);",
"- $result = $stmt->fetch(\\PDO::FETCH_ASSOC);",
"-",
"- return $result !== false ? $result : null;",
"- }",
"-",
"- private function getStatistics(): array",
"- {",
"- $stats = ['total' => 0, 'by_type' => [], 'by_status' => []];",
"-",
"- $stmt = $this->db->query('SELECT type, COUNT(*) as cnt FROM content_config GROUP BY type');",
"- foreach ($stmt->fetchAll(\\PDO::FETCH_ASSOC) as $row) {",
"- $stats['by_type'][$row['type']] = (int) $row['cnt'];",
"- $stats['total'] += (int) $row['cnt'];",
"- }",
"-",
"- $stmt = $this->db->query('SELECT status, COUNT(*) as cnt FROM content_config GROUP BY status');",
"- foreach ($stmt->fetchAll(\\PDO::FETCH_ASSOC) as $row) {",
"- $stats['by_status'][$row['status']] = (int) $row['cnt'];",
"- }",
"-",
"- return $stats;",
"- }",
"-",
"- private function getParentOptions(?int $excludeId = null): array",
"- {",
"- $sql = 'SELECT id, type, name FROM content_config WHERE type IN (\"contract\", \"organization\") ORDER BY type, name';",
"- $stmt = $this->db->query($sql);",
"- $parents = [];",
"- foreach ($stmt->fetchAll(\\PDO::FETCH_ASSOC) as $row) {",
"- if ($excludeId !== null && (int) $row['id'] === $excludeId) {",
"- continue;",
"- }",
"- $parents[] = $row;",
"- }",
"-",
"- return $parents;",
"- }",
"-",
"- private function getChildren(int $parentId): array",
"- {",
"- $stmt = $this->db->prepare('SELECT * FROM content_config WHERE parent_id = ? ORDER BY name');",
"- $stmt->execute([$parentId]);",
"-",
"- return $stmt->fetchAll(\\PDO::FETCH_ASSOC);",
"- }",
"-",
"- private function getHistory(int $configId): array",
"- {",
"- $stmt = $this->db->prepare(",
"- 'SELECT * FROM content_config_history WHERE config_id = ? ORDER BY created_at DESC LIMIT 10'",
"- );",
"- $stmt->execute([$configId]);",
"-",
"- return $stmt->fetchAll(\\PDO::FETCH_ASSOC);",
"- }",
" }"
]
}
],
"userModified": false,
"replaceAll": false
}
}