getConnection(); // Tabelle für Beiträge $db->exec(" CREATE TABLE articles ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, content TEXT, author TEXT, category TEXT, tags TEXT, created TIMESTAMP DEFAULT CURRENT_TIMESTAMP );"); // Tabelle für Likes $db->exec(" CREATE TABLE likes ( article_id INTEGER, user_id TEXT, PRIMARY KEY (article_id, user_id), FOREIGN KEY (article_id) REFERENCES articles(id) ON DELETE CASCADE );"); unset($db); } catch (PDOException $e) { throw new InternalServerErrorException($e->getMessage()); } } } /** * Baut die Verbindung zur Datenbank auf. * @throws InternalServerErrorException */ private function getConnection() { try { $user = 'root'; $pw = null; $dsn = 'sqlite:' . __DIR__ . '/../../db/articles.db'; return new PDO($dsn, $user, $pw); } catch (PDOException $e) { throw new InternalServerErrorException($e->getMessage()); } } /** * Gibt die DatabaseArticleManager-Instanz zurück. * @return DatabaseArticleManager */ public static function getInstance() { if (self::$instance == null) { self::$instance = new DatabaseArticleManager(); } return self::$instance; } public function addArticle($title, $content, $author, $category, $tags) { try { $db = $this->getConnection(); $sql = "INSERT INTO articles (title, content, author, category, tags) VALUES (:title, :content, :author, :category, :tags);"; $command = $db->prepare($sql); if (!$command) { throw new InternalServerErrorException("internal_error"); } // Verknüpft die übergebenen Parameter exakt mit den SQL-Platzhaltern $success = $command->execute([ ":title" => $title, ":content" => $content, ":author" => $author, ":category" => $category, ":tags" => $tags ]); if (!$success) { throw new InternalServerErrorException("internal_error"); } return intval($db->lastInsertId()); } catch (PDOException $e) { throw new InternalServerErrorException($e->getMessage()); } } public function updateArticle($id, $article, $author) { if (empty($article)) { throw new InternalServerErrorException("internal_error"); } // Berechtigungsprüfung analog zur lokalen Implementierung: if ($article->getAuthor() !== $author) { throw new UnauthorizedAccessException("unauthorized_access"); } try { $db = $this->getConnection(); $sql = "UPDATE articles SET title = :title, content = :content, author = :author, category = :category, tags = :tags WHERE id = :id;"; $command = $db->prepare($sql); if (!$command) { throw new InternalServerErrorException("internal_error"); } $success = $command->execute([ ":id" => $id, ":title" => $article->getTitle(), ":content" => $article->getContent(), ":author" => $author, ":category" => $article->getCategory(), ":tags" => $article->getTags() ]); // rowCount() prüft, ob eine Zeile mit dieser ID existierte und geändert werden konnte if (!$success || $command->rowCount() === 0) { // Falls die ID nicht existiert, prüfen wir, ob sie überhaupt da ist if (!$this->getArticle($id)) { throw new NotFoundException("missing_id"); } } } catch (PDOException $e) { throw new InternalServerErrorException("internal_error"); } } public function deleteArticle($id, $author) { $article = $this->getArticle($id); if (empty($article)) { throw new NotFoundException("not_found_article"); } // Berechtigungsprüfung: if ($article->getAuthor() !== $author) { throw new UnauthorizedAccessException("unauthorized_access"); } try { $db = $this->getConnection(); $sql = "DELETE FROM articles WHERE id = :id;"; $command = $db->prepare($sql); if (!$command) { throw new InternalServerErrorException("internal_error"); } if (!$command->execute([":id" => $id])) { throw new InternalServerErrorException("internal_error"); } } catch (PDOException $exc) { throw new InternalServerErrorException("internal_error"); } } public function getArticle($id) { try { $db = $this->getConnection(); $sql = "SELECT * FROM articles WHERE id = :id;"; $command = $db->prepare($sql); if (!$command) { throw new InternalServerErrorException("internal_error"); } $command->execute([":id" => $id]); $row = $command->fetch(PDO::FETCH_ASSOC); if ($row) { $likes = $this->getLikesForArticle(intval($row['id'])); return new Article( intval($row['id']), $row['title'], $row['content'], $row['author'], $row['category'], $row['tags'], $row['created'], $likes ); } return null; } catch (PDOException $e) { throw new InternalServerErrorException("internal_error"); } } public function getAllArticles() { try { $db = $this->getConnection(); $sql = "SELECT * FROM articles;"; $command = $db->query($sql); if (!$command) { throw new InternalServerErrorException("internal_error"); } $rows = $command->fetchAll(PDO::FETCH_ASSOC); $articles = []; foreach ($rows as $row) { $articles[] = new Article( intval($row['id']), $row['title'], $row['content'], $row['author'], $row['category'], $row['tags'], $row['created'] ); } return $articles; } catch (PDOException $e) { throw new InternalServerErrorException("internal_error"); } } public function getArticlesByAuthor($author) { try { $db = $this->getConnection(); $sql = "SELECT * FROM articles WHERE author = :author;"; $command = $db->prepare($sql); if (!$command) { throw new InternalServerErrorException("internal_error"); } $command->execute([":author" => $author]); $rows = $command->fetchAll(PDO::FETCH_ASSOC); $filteredArticles = []; foreach ($rows as $row) { $likes = $this->getLikesForArticle(intval($row['id'])); $filteredArticles[] = new Article( intval($row['id']), $row['title'], $row['content'], $row['author'], $row['category'], $row['tags'], $row['created'], $likes ); } return $filteredArticles; } catch (PDOException $e) { throw new InternalServerErrorException("internal_error"); } } public function getArticlesByCategory($category) { try { $db = $this->getConnection(); $sql = "SELECT * FROM articles WHERE category = :category;"; $command = $db->prepare($sql); if (!$command) { throw new InternalServerErrorException("internal_error"); } $command->execute([":category" => $category]); $rows = $command->fetchAll(PDO::FETCH_ASSOC); $filteredArticles = []; foreach ($rows as $row) { $likes = $this->getLikesForArticle(intval($row['id'])); $filteredArticles[] = new Article( intval($row['id']), $row['title'], $row['content'], $row['author'], $row['category'], $row['tags'], $row['created'], $likes ); } return $filteredArticles; } catch (PDOException $exc) { throw new InternalServerErrorException("internal_error"); } } public function search(string $keyword): array { $cleankeyword = trim($keyword); // Leeren Suchbegriff sofort abfangen if ($cleankeyword === '') { return []; } try { $db = $this->getConnection(); $sql = "SELECT id, title, content, author, category, tags, created FROM articles WHERE title LIKE :keyword OR content LIKE :keyword;"; $command = $db->prepare($sql); if (!$command) { throw new InternalServerErrorException("internal_error"); } // Wildcards für die Suche hinzufügen $searchParam = '%' . $cleankeyword . '%'; $success = $command->execute([ ":keyword" => $searchParam ]); if (!$success) { throw new InternalServerErrorException("internal_error"); } $rows = $command->fetchAll(PDO::FETCH_ASSOC); $filteredArticles = []; foreach ($rows as $row) { $likes = $this->getLikesForArticle(intval($row['id'])); $filteredArticles[] = new Article( intval($row['id']), $row['title'] ?? '', $row['content'] ?? '', $row['author'] ?? '', $row['category'] ?? '', $row['tags'] ?? '', $row['created'] ?? '', $likes ); } return $filteredArticles; } catch (PDOException $e) { throw new InternalServerErrorException("internal_error"); } } /** * Holt alle User-IDs, die einen bestimmten Beitrag geliked haben. * * @return String[] UserIDs * @throws InternalServerErrorException */ private function getLikesForArticle(int $articleId): array { try { $db = $this->getConnection(); $sql = "SELECT user_id FROM likes WHERE article_id = :article_id;"; $command = $db->prepare($sql); $command->execute([':article_id' => $articleId]); return $command->fetchAll(PDO::FETCH_COLUMN) ?: []; } catch (PDOException $e) { return []; } } public function toggleLike(int $articleId, string $userId): bool { // prüfen, ob der Artikel überhaupt existiert $article = $this->getArticle($articleId); if (!$article) { throw new NotFoundException("missing_id"); } try { $db = $this->getConnection(); // prüfen, ob das Like bereits existiert $checkSql = "SELECT COUNT(*) FROM likes WHERE article_id = :article_id AND user_id = :user_id;"; $checkCommand = $db->prepare($checkSql); $checkCommand->execute([ ':article_id' => $articleId, ':user_id' => $userId ]); $hasLiked = (int)$checkCommand->fetchColumn() > 0; if ($hasLiked) { // wenn bereits geliked -> Unlike $deleteSql = "DELETE FROM likes WHERE article_id = :article_id AND user_id = :user_id;"; $deleteCommand = $db->prepare($deleteSql); $deleteCommand->execute([ ':article_id' => $articleId, ':user_id' => $userId ]); return false; // gibt false zurück, da der Beitrag jetzt nicht mehr geliked ist } else { // wenn noch nicht geliked -> Like $insertSql = "INSERT INTO likes (article_id, user_id) VALUES (:article_id, :user_id);"; $insertCommand = $db->prepare($insertSql); $insertCommand->execute([ ':article_id' => $articleId, ':user_id' => $userId ]); return true; // gibt true zurück, da der Beitrag jetzt geliked ist } } catch (PDOException $e) { throw new InternalServerErrorException("internal_error"); } } }