misuzu/src/Comments/CommentsPostsData.php

273 lines
9.9 KiB
PHP

<?php
namespace Misuzu\Comments;
use InvalidArgumentException;
use RuntimeException;
use Index\Db\{DbConnection,DbStatementCache};
use Misuzu\Users\UserInfo;
class CommentsPostsData {
private DbStatementCache $cache;
public function __construct(DbConnection $dbConn) {
$this->cache = new DbStatementCache($dbConn);
}
public function countPosts(
CommentsCategoryInfo|string|null $categoryInfo = null,
?string $categoryName = null,
CommentsPostInfo|string|null $parentInfo = null,
UserInfo|string|null $userInfo = null,
?bool $replies = null,
?bool $deleted = null
): int {
if($categoryInfo instanceof CommentsCategoryInfo)
$categoryInfo = $categoryInfo->id;
if($parentInfo instanceof CommentsPostInfo)
$parentInfo = $parentInfo->id;
$hasCategoryInfo = $categoryInfo !== null;
$hasCategoryName = $categoryName !== null;
$hasParentInfo = $parentInfo !== null;
$hasUserInfo = $userInfo !== null;
$hasReplies = $replies !== null;
$hasDeleted = $deleted !== null;
$args = 0;
$query = 'SELECT COUNT(*) FROM msz_comments_posts';
if($hasParentInfo) {
++$args;
$query .= ' WHERE comment_reply_to = ?';
}
if($hasCategoryInfo)
$query .= sprintf(' %s category_id = ?', ++$args > 1 ? 'AND' : 'WHERE');
if($hasCategoryName)
$query .= sprintf(' %s category_id = (SELECT category_id FROM msz_comments_categories WHERE category_name = ?)', ++$args > 1 ? 'AND' : 'WHERE');
if($hasReplies)
$query .= sprintf(' %s comment_reply_to %s NULL', ++$args > 1 ? 'AND' : 'WHERE', $replies ? 'IS NOT' : 'IS');
if($hasDeleted)
$query .= sprintf(' %s comment_deleted %s NULL', ++$args > 1 ? 'AND' : 'WHERE', $deleted ? 'IS NOT' : 'IS');
if($hasUserInfo)
$query .= sprintf(' %s user_id = ?', ++$args > 1 ? 'AND' : 'WHERE');
$stmt = $this->cache->get($query);
if($hasParentInfo)
$stmt->nextParameter($parentInfo);
elseif($hasCategoryInfo)
$stmt->nextParameter($categoryInfo);
if($hasCategoryName)
$stmt->nextParameter($categoryName);
if($hasUserInfo)
$stmt->nextParameter($userInfo instanceof UserInfo ? $userInfo->id : $userInfo);
$stmt->execute();
$result = $stmt->getResult();
$count = 0;
if($result->next())
$count = $result->getInteger(0);
return $count;
}
/** @return \Iterator<int, CommentsPostInfo> */
public function getPosts(
CommentsCategoryInfo|string|null $categoryInfo = null,
?string $categoryName = null,
CommentsPostInfo|string|null $parentInfo = null,
UserInfo|string|null $userInfo = null,
?bool $replies = null,
?bool $deleted = null
): iterable {
if($categoryInfo instanceof CommentsCategoryInfo)
$categoryInfo = $categoryInfo->id;
if($parentInfo instanceof CommentsPostInfo)
$parentInfo = $parentInfo->id;
$hasCategoryInfo = $categoryInfo !== null;
$hasCategoryName = $categoryName !== null;
$hasParentInfo = $parentInfo !== null;
$hasUserInfo = $userInfo !== null;
$hasReplies = $replies !== null;
$hasDeleted = $deleted !== null;
$args = 0;
$query = <<<SQL
SELECT comment_id, category_id, user_id, comment_reply_to, comment_text,
UNIX_TIMESTAMP(comment_created),
UNIX_TIMESTAMP(comment_pinned),
UNIX_TIMESTAMP(comment_edited),
UNIX_TIMESTAMP(comment_deleted)
FROM msz_comments_posts
SQL;
if($hasParentInfo) {
++$args;
$query .= ' WHERE comment_reply_to = ?';
}
if($hasCategoryInfo)
$query .= sprintf(' %s category_id = ?', ++$args > 1 ? 'AND' : 'WHERE');
if($hasCategoryName)
$query .= sprintf(' %s category_id = (SELECT category_id FROM msz_comments_categories WHERE category_name = ?)', ++$args > 1 ? 'AND' : 'WHERE');
if($hasReplies)
$query .= sprintf(' %s comment_reply_to %s NULL', ++$args > 1 ? 'AND' : 'WHERE', $replies ? 'IS NOT' : 'IS');
if($hasDeleted)
$query .= sprintf(' %s comment_deleted %s NULL', ++$args > 1 ? 'AND' : 'WHERE', $deleted ? 'IS NOT' : 'IS');
if($hasUserInfo)
$query .= sprintf(' %s user_id = ?', ++$args > 1 ? 'AND' : 'WHERE');
// this should really not be implicit like this
if($hasParentInfo)
$query .= ' ORDER BY comment_deleted ASC, comment_pinned DESC, comment_created ASC';
elseif($hasCategoryInfo)
$query .= ' ORDER BY comment_deleted ASC, comment_pinned DESC, comment_created DESC';
else
$query .= ' ORDER BY comment_created DESC';
$stmt = $this->cache->get($query);
if($hasParentInfo)
$stmt->nextParameter($parentInfo);
elseif($hasCategoryInfo)
$stmt->nextParameter($categoryInfo);
if($hasCategoryName)
$stmt->nextParameter($categoryName);
if($hasUserInfo)
$stmt->nextParameter($userInfo instanceof UserInfo ? $userInfo->id : $userInfo);
$stmt->execute();
return $stmt->getResultIterator(CommentsPostInfo::fromResult(...));
}
public function getPost(string $postId): CommentsPostInfo {
$stmt = $this->cache->get(<<<SQL
SELECT comment_id, category_id, user_id, comment_reply_to, comment_text,
UNIX_TIMESTAMP(comment_created),
UNIX_TIMESTAMP(comment_pinned),
UNIX_TIMESTAMP(comment_edited),
UNIX_TIMESTAMP(comment_deleted)
FROM msz_comments_posts
WHERE comment_id = ?
SQL);
$stmt->nextParameter($postId);
$stmt->execute();
$result = $stmt->getResult();
if(!$result->next())
throw new RuntimeException('No comment with that ID exists.');
return CommentsPostInfo::fromResult($result);
}
public function createPost(
CommentsCategoryInfo|string|null $category,
CommentsPostInfo|string|null $parent,
UserInfo|string|null $user,
string $body,
bool $pin = false
): CommentsPostInfo {
if($category instanceof CommentsCategoryInfo)
$category = $category->id;
if($parent instanceof CommentsPostInfo) {
if($category === null)
$category = $parent->categoryId;
elseif($category !== $parent->categoryId)
throw new InvalidArgumentException('$parent belongs to a different category than where this post is attempted to be created.');
$parent = $parent->id;
}
if($category === null)
throw new InvalidArgumentException('$category is null; at least a $category or $parent must be specified.');
if($user instanceof UserInfo)
$user = $user->id;
if(empty(trim($body)))
throw new InvalidArgumentException('$body may not be empty.');
$stmt = $this->cache->get(<<<SQL
INSERT INTO msz_comments_posts (
category_id, user_id, comment_reply_to, comment_text, comment_pinned
) VALUES (?, ?, ?, ?, IF(?, NOW(), NULL))
SQL);
$stmt->nextParameter($category);
$stmt->nextParameter($user);
$stmt->nextParameter($parent);
$stmt->nextParameter($body);
$stmt->nextParameter($pin ? 1 : 0);
$stmt->execute();
return $this->getPost((string)$stmt->lastInsertId);
}
public function deletePost(CommentsPostInfo|string $infoOrId): void {
if($infoOrId instanceof CommentsPostInfo)
$infoOrId = $infoOrId->id;
$stmt = $this->cache->get(<<<SQL
UPDATE msz_comments_posts
SET comment_deleted = COALESCE(comment_deleted, NOW())
WHERE comment_id = ?
SQL);
$stmt->nextParameter($infoOrId);
$stmt->execute();
}
public function nukePost(CommentsPostInfo|string $infoOrId): void {
if($infoOrId instanceof CommentsPostInfo)
$infoOrId = $infoOrId->id;
$stmt = $this->cache->get(<<<SQL
DELETE FROM msz_comments_posts
WHERE comment_id = ?
SQL);
$stmt->nextParameter($infoOrId);
$stmt->execute();
}
public function restorePost(CommentsPostInfo|string $infoOrId): void {
if($infoOrId instanceof CommentsPostInfo)
$infoOrId = $infoOrId->id;
$stmt = $this->cache->get(<<<SQL
UPDATE msz_comments_posts
SET comment_deleted = NULL
WHERE comment_id = ?
SQL);
$stmt->nextParameter($infoOrId);
$stmt->execute();
}
public function updatePost(
CommentsPostInfo|string $infoOrId,
?string $body,
?bool $pinned,
bool $edited = false
): void {
if($infoOrId instanceof CommentsPostInfo)
$infoOrId = $infoOrId->id;
$fields = [];
$values = [];
if($body !== null) {
if(trim($body) === '')
throw new InvalidArgumentException('$body must be null or a non-empty string.');
$fields[] = 'comment_text = ?';
$values[] = $body;
}
if($pinned !== null)
$fields[] = $pinned ? 'comment_pinned = COALESCE(comment_pinned, NOW())' : 'comment_pinned = NULL';
if($edited)
$fields[] = 'comment_edited = NOW()';
if(empty($fields))
return;
$stmt = $this->cache->get(sprintf('UPDATE msz_comments_posts SET %s WHERE comment_id = ?', implode(', ', $fields)));
foreach($values as $value)
$stmt->nextParameter($value);
$stmt->nextParameter($infoOrId);
$stmt->execute();
}
}