3d50e6e3b3
Einführung einer dynamischen Like-Funktion für Beiträge
449 lines
14 KiB
PHP
449 lines
14 KiB
PHP
<?php
|
|
require_once 'ArticleManagerDAO.php';
|
|
require_once 'Article.php';
|
|
/**
|
|
* Klasse: Eine SQLLite3 Lösung des ArticleManagerDAO.
|
|
*
|
|
* @author Niklas Ortmann
|
|
*/
|
|
class DatabaseArticleManager implements ArticleManagerDAO {
|
|
|
|
private static $instance = null;
|
|
|
|
/**
|
|
* Konstruktor
|
|
*
|
|
* Erstellt die Datenbankverbindung und die Tabelle articles.
|
|
* @throws InternalServerErrorException
|
|
*/
|
|
public function __construct()
|
|
{
|
|
if (!file_exists(__DIR__ . '/../../db/articles.db')) {
|
|
try {
|
|
|
|
$db = $this->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");
|
|
}
|
|
}
|
|
} |