406 lines
15 KiB
PHP
406 lines
15 KiB
PHP
<?php
|
|
namespace Misuzu\News;
|
|
|
|
use InvalidArgumentException;
|
|
use RuntimeException;
|
|
use Index\DateTime;
|
|
use Index\Data\DbStatementCache;
|
|
use Index\Data\IDbConnection;
|
|
use Index\Data\IDbResult;
|
|
use Misuzu\Pagination;
|
|
use Misuzu\Comments\CommentsCategoryInfo;
|
|
use Misuzu\Users\UserInfo;
|
|
|
|
class News {
|
|
private IDbConnection $dbConn;
|
|
private DbStatementCache $cache;
|
|
|
|
public function __construct(IDbConnection $dbConn) {
|
|
$this->dbConn = $dbConn;
|
|
$this->cache = new DbStatementCache($dbConn);
|
|
}
|
|
|
|
public function countCategories(
|
|
?bool $hidden = null
|
|
): int {
|
|
$hasHidden = $hidden !== null;
|
|
|
|
$query = 'SELECT COUNT(*) FROM msz_news_categories';
|
|
if($hasHidden)
|
|
$query .= sprintf(' WHERE category_is_hidden %s 0', $hidden ? '<>' : '=');
|
|
|
|
$result = $this->dbConn->query($query);
|
|
$count = 0;
|
|
|
|
if($result->next())
|
|
$count = $result->getInteger(0);
|
|
|
|
return $count;
|
|
}
|
|
|
|
public function getCategories(
|
|
?bool $hidden = null,
|
|
?Pagination $pagination = null
|
|
): iterable {
|
|
$hasHidden = $hidden !== null;
|
|
$hasPagination = $pagination !== null;
|
|
|
|
$query = 'SELECT category_id, category_name, category_description, category_is_hidden, UNIX_TIMESTAMP(category_created), (SELECT COUNT(*) FROM msz_news_posts AS np WHERE np.category_id = nc.category_id) AS category_posts_count FROM msz_news_categories AS nc';
|
|
if($hasHidden)
|
|
$query .= sprintf(' WHERE category_is_hidden %s 0', $hidden ? '<>' : '=');
|
|
$query .= ' ORDER BY category_created ASC';
|
|
if($hasPagination)
|
|
$query .= ' LIMIT ? OFFSET ?';
|
|
$stmt = $this->cache->get($query);
|
|
|
|
$args = 0;
|
|
if($hasPagination) {
|
|
$stmt->addParameter(++$args, $pagination->getRange());
|
|
$stmt->addParameter(++$args, $pagination->getOffset());
|
|
}
|
|
|
|
$stmt->execute();
|
|
|
|
return $stmt->getResult()->getIterator(NewsCategoryInfo::fromResult(...));
|
|
}
|
|
|
|
public function getCategory(
|
|
?string $categoryId = null,
|
|
NewsPostInfo|string|null $postInfo = null
|
|
): NewsCategoryInfo {
|
|
$hasCategoryId = $categoryId !== null;
|
|
$hasPostInfo = $postInfo !== null;
|
|
|
|
if(!$hasCategoryId && !$hasPostInfo)
|
|
throw new InvalidArgumentException('At least one argument must be specified.');
|
|
if($hasCategoryId && $hasPostInfo)
|
|
throw new InvalidArgumentException('Only one argument may be specified.');
|
|
|
|
$value = null;
|
|
$query = 'SELECT category_id, category_name, category_description, category_is_hidden, UNIX_TIMESTAMP(category_created) FROM msz_news_categories';
|
|
|
|
if($hasCategoryId) {
|
|
$query .= ' WHERE category_id = ?';
|
|
$value = $categoryId;
|
|
}
|
|
if($hasPostInfo) {
|
|
if($postInfo instanceof NewsPostInfo) {
|
|
$query .= ' WHERE category_id = ?';
|
|
$value = $postInfo->getCategoryId();
|
|
} else {
|
|
$query .= ' WHERE category_id = (SELECT category_id FROM msz_news_posts WHERE post_id = ?)';
|
|
$value = $postInfo;
|
|
}
|
|
}
|
|
|
|
$stmt = $this->cache->get($query);
|
|
$stmt->addParameter(1, $value);
|
|
$stmt->execute();
|
|
|
|
$result = $stmt->getResult();
|
|
if(!$result->next())
|
|
throw new RuntimeException('News category not found.');
|
|
|
|
return NewsCategoryInfo::fromResult($result);
|
|
}
|
|
|
|
public function createCategory(
|
|
string $name,
|
|
string $description,
|
|
bool $hidden
|
|
): NewsCategoryInfo {
|
|
$name = trim($name);
|
|
if(empty($name))
|
|
throw new InvalidArgumentException('$name may not be empty');
|
|
|
|
$description = trim($description);
|
|
if(empty($description))
|
|
throw new InvalidArgumentException('$description may not be empty');
|
|
|
|
$stmt = $this->cache->get('INSERT INTO msz_news_categories (category_name, category_description, category_is_hidden) VALUES (?, ?, ?)');
|
|
$stmt->addParameter(1, $name);
|
|
$stmt->addParameter(2, $description);
|
|
$stmt->addParameter(3, $hidden ? 1 : 0);
|
|
$stmt->execute();
|
|
|
|
return $this->getCategory(categoryId: (string)$this->dbConn->getLastInsertId());
|
|
}
|
|
|
|
public function deleteCategory(NewsCategoryInfo|string $infoOrId): void {
|
|
if($infoOrId instanceof NewsCategoryInfo)
|
|
$infoOrId = $infoOrId->getId();
|
|
|
|
$stmt = $this->cache->get('DELETE FROM msz_news_categories WHERE category_id = ?');
|
|
$stmt->addParameter(1, $infoOrId);
|
|
$stmt->execute();
|
|
}
|
|
|
|
public function updateCategory(
|
|
NewsCategoryInfo|string $infoOrId,
|
|
?string $name = null,
|
|
?string $description = null,
|
|
?bool $hidden = null
|
|
): void {
|
|
if($infoOrId instanceof NewsCategoryInfo)
|
|
$infoOrId = $infoOrId->getId();
|
|
|
|
if($name !== null) {
|
|
$name = trim($name);
|
|
if(empty($name))
|
|
throw new InvalidArgumentException('$name may not be empty');
|
|
}
|
|
|
|
if($description !== null) {
|
|
$description = trim($description);
|
|
if(empty($description))
|
|
throw new InvalidArgumentException('$description may not be empty');
|
|
}
|
|
|
|
$hasHidden = $hidden !== null;
|
|
|
|
$stmt = $this->cache->get('UPDATE msz_news_categories SET category_name = COALESCE(?, category_name), category_description = COALESCE(?, category_description), category_is_hidden = IF(?, ?, category_is_hidden) WHERE category_id = ?');
|
|
$stmt->addParameter(1, $name);
|
|
$stmt->addParameter(2, $description);
|
|
$stmt->addParameter(3, $hasHidden ? 1 : 0);
|
|
$stmt->addParameter(4, $hidden ? 1 : 0);
|
|
$stmt->addParameter(5, $infoOrId);
|
|
$stmt->execute();
|
|
}
|
|
|
|
public function countPosts(
|
|
NewsCategoryInfo|string|null $categoryInfo = null,
|
|
bool $onlyFeatured = false,
|
|
bool $includeScheduled = false,
|
|
bool $includeDeleted = false
|
|
): int {
|
|
if($categoryInfo instanceof NewsCategoryInfo)
|
|
$categoryInfo = $categoryInfo->getId();
|
|
|
|
$hasCategoryInfo = $categoryInfo !== null;
|
|
|
|
$args = 0;
|
|
$query = 'SELECT COUNT(*) FROM msz_news_posts';
|
|
if($hasCategoryInfo) {
|
|
++$args;
|
|
$query .= ' WHERE category_id = ?';
|
|
}
|
|
if($onlyFeatured) {
|
|
$query .= (++$args > 1 ? ' AND' : ' WHERE');
|
|
$query .= ' post_is_featured = 1';
|
|
}
|
|
if(!$includeScheduled) {
|
|
$query .= (++$args > 1 ? ' AND' : ' WHERE');
|
|
$query .= ' post_scheduled <= NOW()';
|
|
}
|
|
if(!$includeDeleted) {
|
|
$query .= (++$args > 1 ? ' AND' : ' WHERE');
|
|
$query .= ' post_deleted IS NULL';
|
|
}
|
|
|
|
$stmt = $this->cache->get($query);
|
|
if($hasCategoryInfo)
|
|
$stmt->addParameter(1, $categoryInfo);
|
|
$stmt->execute();
|
|
|
|
$result = $stmt->getResult();
|
|
$count = 0;
|
|
|
|
if($result->next())
|
|
$count = $result->getInteger(0);
|
|
|
|
return $count;
|
|
}
|
|
|
|
public function getPosts(
|
|
NewsCategoryInfo|string|null $categoryInfo = null,
|
|
string $searchQuery = null,
|
|
bool $onlyFeatured = false,
|
|
bool $includeScheduled = false,
|
|
bool $includeDeleted = false,
|
|
?Pagination $pagination = null
|
|
): iterable {
|
|
if($categoryInfo instanceof NewsCategoryInfo)
|
|
$categoryInfo = $categoryInfo->getId();
|
|
|
|
$hasCategoryInfo = $categoryInfo !== null;
|
|
$hasSearchQuery = $searchQuery !== null;
|
|
$hasPagination = $pagination !== null;
|
|
|
|
$args = 0;
|
|
$query = 'SELECT post_id, category_id, user_id, comment_section_id, post_is_featured, post_title, post_text, UNIX_TIMESTAMP(post_scheduled), UNIX_TIMESTAMP(post_created), UNIX_TIMESTAMP(post_updated), UNIX_TIMESTAMP(post_deleted) FROM msz_news_posts';
|
|
if($hasCategoryInfo) {
|
|
++$args;
|
|
$query .= ' WHERE category_id = ?';
|
|
}
|
|
if($hasSearchQuery) {
|
|
$query .= (++$args > 1 ? ' AND' : ' WHERE');
|
|
$query .= ' MATCH(post_title, post_text) AGAINST (? IN NATURAL LANGUAGE MODE)';
|
|
}
|
|
if($onlyFeatured) {
|
|
$query .= (++$args > 1 ? ' AND' : ' WHERE');
|
|
$query .= ' post_is_featured = 1';
|
|
}
|
|
if(!$includeScheduled) {
|
|
$query .= (++$args > 1 ? ' AND' : ' WHERE');
|
|
$query .= ' post_scheduled <= NOW()';
|
|
}
|
|
if(!$includeDeleted) {
|
|
$query .= (++$args > 1 ? ' AND' : ' WHERE');
|
|
$query .= ' post_deleted IS NULL';
|
|
}
|
|
$query .= ' ORDER BY post_scheduled DESC';
|
|
if($hasPagination)
|
|
$query .= ' LIMIT ? OFFSET ?';
|
|
$stmt = $this->cache->get($query);
|
|
|
|
$args = 0;
|
|
if($hasCategoryInfo)
|
|
$stmt->addParameter(++$args, $categoryInfo);
|
|
if($hasSearchQuery)
|
|
$stmt->addParameter(++$args, $searchQuery);
|
|
if($hasPagination) {
|
|
$stmt->addParameter(++$args, $pagination->getRange());
|
|
$stmt->addParameter(++$args, $pagination->getOffset());
|
|
}
|
|
|
|
$stmt->execute();
|
|
|
|
return $stmt->getResult()->getIterator(NewsPostInfo::fromResult(...));
|
|
}
|
|
|
|
public function getPost(string $postId): NewsPostInfo {
|
|
$stmt = $this->cache->get('SELECT post_id, category_id, user_id, comment_section_id, post_is_featured, post_title, post_text, UNIX_TIMESTAMP(post_scheduled), UNIX_TIMESTAMP(post_created), UNIX_TIMESTAMP(post_updated), UNIX_TIMESTAMP(post_deleted) FROM msz_news_posts WHERE post_id = ?');
|
|
$stmt->addParameter(1, $postId);
|
|
$stmt->execute();
|
|
|
|
$result = $stmt->getResult();
|
|
if(!$result->next())
|
|
throw new RuntimeException('No news post with that ID exists.');
|
|
|
|
return NewsPostInfo::fromResult($result);
|
|
}
|
|
|
|
public function createPost(
|
|
NewsCategoryInfo|string $categoryInfo,
|
|
string $title,
|
|
string $body,
|
|
bool $featured = false,
|
|
UserInfo|string|null $userInfo = null,
|
|
DateTime|int|null $schedule = null
|
|
): NewsPostInfo {
|
|
if($categoryInfo instanceof NewsCategoryInfo)
|
|
$categoryInfo = $categoryInfo->getId();
|
|
if($userInfo instanceof UserInfo)
|
|
$userInfo = $userInfo->getId();
|
|
if($schedule instanceof DateTime)
|
|
$schedule = $schedule->getUnixTimeSeconds();
|
|
|
|
$title = trim($title);
|
|
if(empty($title))
|
|
throw new InvalidArgumentException('$title may not be empty');
|
|
|
|
$body = trim($body);
|
|
if(empty($body))
|
|
throw new InvalidArgumentException('$body may not be empty');
|
|
|
|
$stmt = $this->cache->get('INSERT INTO msz_news_posts (category_id, user_id, post_is_featured, post_title, post_text, post_scheduled) VALUES (?, ?, ?, ?, ?, ?)');
|
|
$stmt->addParameter(1, $categoryInfo);
|
|
$stmt->addParameter(2, $userInfo);
|
|
$stmt->addParameter(3, $featured ? 1 : 0);
|
|
$stmt->addParameter(4, $title);
|
|
$stmt->addParameter(5, $body);
|
|
$stmt->addParameter(6, $schedule);
|
|
$stmt->execute();
|
|
|
|
return $this->getPost((string)$this->dbConn->getLastInsertId());
|
|
}
|
|
|
|
public function deletePost(NewsPostInfo|string $postInfo): void {
|
|
if($postInfo instanceof NewsPostInfo)
|
|
$postInfo = $postInfo->getId();
|
|
|
|
$stmt = $this->cache->get('UPDATE msz_news_posts SET post_deleted = COALESCE(post_deleted, NOW()) WHERE post_id = ?');
|
|
$stmt->addParameter(1, $postInfo);
|
|
$stmt->execute();
|
|
}
|
|
|
|
public function restorePost(NewsPostInfo|string $postInfo): void {
|
|
if($postInfo instanceof NewsPostInfo)
|
|
$postInfo = $postInfo->getId();
|
|
|
|
$stmt = $this->cache->get('UPDATE msz_news_posts SET post_deleted = NULL WHERE post_id = ?');
|
|
$stmt->addParameter(1, $postInfo);
|
|
$stmt->execute();
|
|
}
|
|
|
|
public function nukePost(NewsPostInfo|string $postInfo): void {
|
|
if($postInfo instanceof NewsPostInfo)
|
|
$postInfo = $postInfo->getId();
|
|
|
|
// should this enforce a soft delete first? (AND post_deleted IS NOT NULL)
|
|
$stmt = $this->cache->get('DELETE FROM msz_news_posts WHERE post_id = ?');
|
|
$stmt->addParameter(1, $postInfo);
|
|
$stmt->execute();
|
|
}
|
|
|
|
public function updatePost(
|
|
NewsPostInfo|string $postInfo,
|
|
NewsCategoryInfo|string|null $categoryInfo = null,
|
|
?string $title = null,
|
|
?string $body = null,
|
|
?bool $featured = null,
|
|
bool $updateUserInfo = false,
|
|
UserInfo|string|null $userInfo = null,
|
|
DateTime|int|null $schedule = null
|
|
): void {
|
|
if($postInfo instanceof NewsPostInfo)
|
|
$postInfo = $postInfo->getId();
|
|
if($categoryInfo instanceof NewsCategoryInfo)
|
|
$categoryInfo = $categoryInfo->getId();
|
|
if($userInfo instanceof UserInfo)
|
|
$userInfo = $userInfo->getId();
|
|
if($schedule instanceof DateTime)
|
|
$schedule = $schedule->getUnixTimeSeconds();
|
|
|
|
if($title !== null) {
|
|
$title = trim($title);
|
|
if(empty($title))
|
|
throw new InvalidArgumentException('$title may not be empty');
|
|
}
|
|
|
|
if($body !== null) {
|
|
$body = trim($body);
|
|
if(empty($body))
|
|
throw new InvalidArgumentException('$body may not be empty');
|
|
}
|
|
|
|
$hasFeatured = $featured !== null;
|
|
|
|
$stmt = $this->cache->get('UPDATE msz_news_posts SET category_id = COALESCE(?, category_id), user_id = IF(?, ?, user_id), post_is_featured = IF(?, ?, post_is_featured), post_title = COALESCE(?, post_title), post_text = COALESCE(?, post_text), post_scheduled = COALESCE(?, post_scheduled) WHERE post_id = ?');
|
|
$stmt->addParameter(1, $categoryInfo);
|
|
$stmt->addParameter(2, $updateUserInfo ? 1 : 0);
|
|
$stmt->addParameter(3, $userInfo);
|
|
$stmt->addParameter(4, $hasFeatured ? 1 : 0);
|
|
$stmt->addParameter(5, $featured ? 1 : 0);
|
|
$stmt->addParameter(6, $title);
|
|
$stmt->addParameter(7, $body);
|
|
$stmt->addParameter(8, $schedule);
|
|
$stmt->addParameter(9, $postInfo);
|
|
$stmt->execute();
|
|
}
|
|
|
|
public function updatePostCommentCategory(
|
|
NewsPostInfo|string $postInfo,
|
|
CommentsCategoryInfo|string $commentsCategory
|
|
): void {
|
|
if($postInfo instanceof NewsPostInfo)
|
|
$postInfo = $postInfo->getId();
|
|
if($commentsCategory instanceof CommentsCategoryInfo)
|
|
$commentsCategory = $commentsCategory->getId();
|
|
|
|
$stmt = $this->cache->get('UPDATE msz_news_posts SET comment_section_id = ? WHERE post_id = ?');
|
|
$stmt->addParameter(1, $commentsCategory);
|
|
$stmt->addParameter(2, $postInfo);
|
|
$stmt->execute();
|
|
}
|
|
}
|