pdo = DatabaseFactory::dev(); } public function findById(int $id): ?array { $stmt = $this->pdo->prepare('SELECT * FROM dokumentation WHERE id = :id'); $stmt->execute(['id' => $id]); $result = $stmt->fetch(); return $result !== false ? $result : null; } public function findByPath(string $path): ?array { $stmt = $this->pdo->prepare('SELECT * FROM dokumentation WHERE path = :path AND status = :status'); $stmt->execute(['path' => $path, 'status' => 'published']); $result = $stmt->fetch(); return $result !== false ? $result : null; } public function findBySlug(string $slug, ?int $parentId = null): ?array { if ($parentId === null) { $stmt = $this->pdo->prepare('SELECT * FROM dokumentation WHERE slug = :slug AND parent_id IS NULL AND status = :status'); $stmt->execute(['slug' => $slug, 'status' => 'published']); } else { $stmt = $this->pdo->prepare('SELECT * FROM dokumentation WHERE slug = :slug AND parent_id = :parent_id AND status = :status'); $stmt->execute(['slug' => $slug, 'parent_id' => $parentId, 'status' => 'published']); } $result = $stmt->fetch(); return $result !== false ? $result : null; } public function findDocBySlug(string $slug): ?array { $stmt = $this->pdo->prepare('SELECT * FROM dokumentation WHERE slug = :slug AND status = :status LIMIT 1'); $stmt->execute(['slug' => $slug, 'status' => 'published']); $result = $stmt->fetch(); return $result !== false ? $result : null; } public function findChildren(int $parentId): array { $stmt = $this->pdo->prepare(' SELECT * FROM dokumentation WHERE parent_id = :parent_id AND status = :status ORDER BY sort_order ASC '); $stmt->execute(['parent_id' => $parentId, 'status' => 'published']); return $stmt->fetchAll(); } public function findRootDocuments(): array { $stmt = $this->pdo->prepare(' SELECT * FROM dokumentation WHERE parent_id IS NULL AND status = :status ORDER BY sort_order ASC '); $stmt->execute(['status' => 'published']); return $stmt->fetchAll(); } public function getHierarchy(): array { $roots = $this->findRootDocuments(); return $this->buildTree($roots); } private function buildTree(array $items): array { $result = []; foreach ($items as $item) { $children = $this->findChildren((int) $item['id']); $item['children'] = $this->buildTree($children); $result[] = $item; } return $result; } public function getBreadcrumb(int $docId): array { $breadcrumb = []; $doc = $this->findById($docId); while ($doc !== null) { array_unshift($breadcrumb, [ 'title' => $doc['title'], 'path' => $doc['path'], ]); if ($doc['parent_id'] !== null) { $doc = $this->findById((int) $doc['parent_id']); } else { $doc = null; } } return $breadcrumb; } public function getSiblings(int $docId): array { $doc = $this->findById($docId); if ($doc === null) { return []; } if ($doc['parent_id'] === null) { return $this->findRootDocuments(); } return $this->findChildren((int) $doc['parent_id']); } public function getStatistics(): array { $stats = []; $stmt = $this->pdo->query('SELECT COUNT(*) FROM dokumentation'); $stats['total'] = (int) $stmt->fetchColumn(); $stmt = $this->pdo->query('SELECT depth, COUNT(*) as count FROM dokumentation GROUP BY depth ORDER BY depth'); $stats['by_depth'] = $stmt->fetchAll(); $stmt = $this->pdo->query('SELECT status, COUNT(*) as count FROM dokumentation GROUP BY status'); $stats['by_status'] = $stmt->fetchAll(); return $stats; } /** * Find all documents with optional filters. */ public function findAll( ?string $status = null, ?int $parentId = null, ?string $search = null, int $limit = 50, int $offset = 0 ): array { $sql = 'SELECT * FROM dokumentation WHERE 1=1'; $params = []; if ($status !== null) { $sql .= ' AND status = :status'; $params['status'] = $status; } if ($parentId !== null) { $sql .= ' AND parent_id = :parent_id'; $params['parent_id'] = $parentId; } if ($search !== null) { $sql .= ' AND (title LIKE :search OR description LIKE :search2)'; $params['search'] = '%' . $search . '%'; $params['search2'] = '%' . $search . '%'; } $sql .= ' ORDER BY depth ASC, sort_order ASC LIMIT :limit OFFSET :offset'; $stmt = $this->pdo->prepare($sql); foreach ($params as $key => $value) { $stmt->bindValue($key, $value); } $stmt->bindValue('limit', $limit, \PDO::PARAM_INT); $stmt->bindValue('offset', $offset, \PDO::PARAM_INT); $stmt->execute(); return $stmt->fetchAll(); } /** * Count documents with optional filters. */ public function count( ?string $status = null, ?int $parentId = null, ?string $search = null ): int { $sql = 'SELECT COUNT(*) FROM dokumentation WHERE 1=1'; $params = []; if ($status !== null) { $sql .= ' AND status = :status'; $params['status'] = $status; } if ($parentId !== null) { $sql .= ' AND parent_id = :parent_id'; $params['parent_id'] = $parentId; } if ($search !== null) { $sql .= ' AND (title LIKE :search OR description LIKE :search2)'; $params['search'] = '%' . $search . '%'; $params['search2'] = '%' . $search . '%'; } $stmt = $this->pdo->prepare($sql); $stmt->execute($params); return (int) $stmt->fetchColumn(); } /** * Create a new document. */ public function create(array $data): int { // Determine path and depth $parentId = $data['parent_id'] ?? null; $slug = $data['slug']; if ($parentId !== null) { $parent = $this->findById((int) $parentId); $path = ($parent['path'] ?? '') . '/' . $slug; $depth = ($parent['depth'] ?? 0) + 1; } else { $path = '/' . $slug; $depth = 0; } $stmt = $this->pdo->prepare(' INSERT INTO dokumentation (parent_id, slug, path, depth, title, description, content, content_format, status, sort_order, created_at, updated_at) VALUES (:parent_id, :slug, :path, :depth, :title, :description, :content, :format, :status, :sort_order, NOW(), NOW()) '); $stmt->execute([ 'parent_id' => $parentId, 'slug' => $slug, 'path' => $path, 'depth' => $depth, 'title' => $data['title'], 'description' => $data['description'] ?? null, 'content' => $data['content'] ?? '', 'format' => $data['content_format'] ?? 'html', 'status' => $data['status'] ?? 'draft', 'sort_order' => $data['sort_order'] ?? 0, ]); return (int) $this->pdo->lastInsertId(); } /** * Update a document. */ public function update(int $id, array $data): void { $fields = []; $params = ['id' => $id]; foreach (['title', 'content', 'description', 'status'] as $field) { if (array_key_exists($field, $data)) { $fields[] = "{$field} = :{$field}"; $params[$field] = $data[$field]; } } if (empty($fields)) { return; } $fields[] = 'updated_at = NOW()'; $fields[] = 'version = version + 1'; $sql = 'UPDATE dokumentation SET ' . implode(', ', $fields) . ' WHERE id = :id'; $stmt = $this->pdo->prepare($sql); $stmt->execute($params); } /** * Delete a document. */ public function delete(int $id): void { $stmt = $this->pdo->prepare('DELETE FROM dokumentation WHERE id = :id'); $stmt->execute(['id' => $id]); } /** * Get document tree (alias for getHierarchy). */ public function getTree(): array { return $this->getHierarchy(); } }