misuzu/src/Messages/MessagesDatabase.php
2024-03-30 03:19:08 +00:00

394 lines
15 KiB
PHP

<?php
namespace Misuzu\Messages;
use InvalidArgumentException;
use RuntimeException;
use Index\DateTime;
use Index\Data\{DbStatementCache,DbTools,IDbConnection};
use Misuzu\Pagination;
use Misuzu\Users\UserInfo;
class MessagesDatabase {
private DbStatementCache $cache;
public function __construct(IDbConnection $dbConn) {
$this->cache = new DbStatementCache($dbConn);
}
public function countMessages(
UserInfo|string|null $ownerInfo = null,
UserInfo|string|null $authorInfo = null,
UserInfo|string|null $recipientInfo = null,
MessageInfo|string|null $repliesFor = null,
MessageInfo|string|null $replyTo = null,
?bool $sent = null,
?bool $read = null,
?bool $deleted = null
): int {
$hasOwnerInfo = $ownerInfo !== null;
$hasAuthorInfo = $authorInfo !== null;
$hasRecipientInfo = $recipientInfo !== null;
$hasRepliesFor = $repliesFor !== null;
$hasReplyTo = $replyTo !== null;
$hasSent = $sent !== null;
$hasRead = $read !== null;
$hasDeleted = $deleted !== null;
$args = 0;
$query = 'SELECT COUNT(*) FROM msz_messages';
if($hasOwnerInfo) {
++$args;
$query .= ' WHERE msg_owner_id = ?';
}
if($hasAuthorInfo)
$query .= sprintf(' %s msg_author_id = ?', ++$args > 1 ? 'AND' : 'WHERE');
if($hasRecipientInfo)
$query .= sprintf(' %s msg_recipient_id = ?', ++$args > 1 ? 'AND' : 'WHERE');
if($hasRepliesFor)
$query .= sprintf(' %s msg_reply_to = ?', ++$args > 1 ? 'AND' : 'WHERE');
if($hasReplyTo) {
$query .= sprintf(' %s msg_id = ', ++$args > 1 ? 'AND' : 'WHERE');
if($replyTo instanceof MessageInfo)
$query .= '?';
else
$query .= '(SELECT reply_to FROM msz_messages WHERE msg_id = ?)';
}
if($hasSent)
$query .= sprintf(' %s msg_sent %s NULL', ++$args > 1 ? 'AND' : 'WHERE', $sent ? 'IS NOT' : 'IS');
if($hasRead)
$query .= sprintf(' %s msg_read %s NULL', ++$args > 1 ? 'AND' : 'WHERE', $read ? 'IS NOT' : 'IS');
if($hasDeleted)
$query .= sprintf(' %s msg_deleted %s NULL', ++$args > 1 ? 'AND' : 'WHERE', $deleted ? 'IS NOT' : 'IS');
$query .= ' ORDER BY msg_created DESC';
$args = 0;
$stmt = $this->cache->get($query);
if($hasOwnerInfo)
$stmt->addParameter(++$args, $ownerInfo instanceof UserInfo ? $ownerInfo->getId() : $ownerInfo);
if($hasAuthorInfo)
$stmt->addParameter(++$args, $authorInfo instanceof UserInfo ? $authorInfo->getId() : $authorInfo);
if($hasRecipientInfo)
$stmt->addParameter(++$args, $recipientInfo instanceof UserInfo ? $recipientInfo->getId() : $recipientInfo);
if($hasRepliesFor)
$stmt->addParameter(++$args, $repliesFor instanceof MessageInfo ? $repliesFor->getId() : $repliesFor);
if($hasReplyTo)
$stmt->addParameter(++$args, $replyTo instanceof MessageInfo ? $replyTo->getReplyToId() : $replyTo);
$stmt->execute();
$result = $stmt->getResult();
return $result->next() ? $result->getInteger(0) : 0;
}
public function getMessages(
UserInfo|string|null $ownerInfo = null,
UserInfo|string|null $authorInfo = null,
UserInfo|string|null $recipientInfo = null,
MessageInfo|string|null $repliesFor = null,
MessageInfo|string|null $replyTo = null,
?bool $sent = null,
?bool $read = null,
?bool $deleted = null,
?Pagination $pagination = null
): iterable {
$hasOwnerInfo = $ownerInfo !== null;
$hasAuthorInfo = $authorInfo !== null;
$hasRecipientInfo = $recipientInfo !== null;
$hasRepliesFor = $repliesFor !== null;
$hasReplyTo = $replyTo !== null;
$hasSent = $sent !== null;
$hasRead = $read !== null;
$hasDeleted = $deleted !== null;
$hasPagination = $pagination !== null;
$args = 0;
$query = 'SELECT msg_id, msg_owner_id, msg_author_id, msg_recipient_id, msg_reply_to, msg_title, msg_body, msg_parser, UNIX_TIMESTAMP(msg_created), UNIX_TIMESTAMP(msg_sent), UNIX_TIMESTAMP(msg_read), UNIX_TIMESTAMP(msg_deleted) FROM msz_messages';
if($hasOwnerInfo) {
++$args;
$query .= ' WHERE msg_owner_id = ?';
}
if($hasAuthorInfo)
$query .= sprintf(' %s msg_author_id = ?', ++$args > 1 ? 'AND' : 'WHERE');
if($hasRecipientInfo)
$query .= sprintf(' %s msg_recipient_id = ?', ++$args > 1 ? 'AND' : 'WHERE');
if($hasRepliesFor)
$query .= sprintf(' %s msg_reply_to = ?', ++$args > 1 ? 'AND' : 'WHERE');
if($hasReplyTo) {
$query .= sprintf(' %s msg_id = ', ++$args > 1 ? 'AND' : 'WHERE');
if($replyTo instanceof MessageInfo)
$query .= '?';
else
$query .= '(SELECT reply_to FROM msz_messages WHERE msg_id = ?)';
}
if($hasSent)
$query .= sprintf(' %s msg_sent %s NULL', ++$args > 1 ? 'AND' : 'WHERE', $sent ? 'IS NOT' : 'IS');
if($hasRead)
$query .= sprintf(' %s msg_read %s NULL', ++$args > 1 ? 'AND' : 'WHERE', $read ? 'IS NOT' : 'IS');
if($hasDeleted)
$query .= sprintf(' %s msg_deleted %s NULL', ++$args > 1 ? 'AND' : 'WHERE', $deleted ? 'IS NOT' : 'IS');
$query .= ' ORDER BY msg_created DESC';
if($hasPagination)
$query .= ' LIMIT ? OFFSET ?';
$args = 0;
$stmt = $this->cache->get($query);
if($hasOwnerInfo)
$stmt->addParameter(++$args, $ownerInfo instanceof UserInfo ? $ownerInfo->getId() : $ownerInfo);
if($hasAuthorInfo)
$stmt->addParameter(++$args, $authorInfo instanceof UserInfo ? $authorInfo->getId() : $authorInfo);
if($hasRecipientInfo)
$stmt->addParameter(++$args, $recipientInfo instanceof UserInfo ? $recipientInfo->getId() : $recipientInfo);
if($hasRepliesFor)
$stmt->addParameter(++$args, $repliesFor instanceof MessageInfo ? $repliesFor->getId() : $repliesFor);
if($hasReplyTo)
$stmt->addParameter(++$args, $replyTo instanceof MessageInfo ? $replyTo->getReplyToId() : $replyTo);
if($hasPagination) {
$stmt->addParameter(++$args, $pagination->getRange());
$stmt->addParameter(++$args, $pagination->getOffset());
}
$stmt->execute();
return $stmt->getResult()->getIterator(MessageInfo::fromResult(...));
}
public function getMessageInfo(
UserInfo|string $ownerInfo,
MessageInfo|string $messageInfoOrId,
bool $useReplyTo = false
): MessageInfo {
$stmt = $this->cache->get(sprintf(
'SELECT msg_id, msg_owner_id, msg_author_id, msg_recipient_id, msg_reply_to, msg_title, msg_body, msg_parser, UNIX_TIMESTAMP(msg_created), UNIX_TIMESTAMP(msg_sent), UNIX_TIMESTAMP(msg_read), UNIX_TIMESTAMP(msg_deleted) FROM msz_messages WHERE msg_id = %s AND msg_owner_id = ?',
!$useReplyTo || $messageInfoOrId instanceof MessageInfo ? '?' : '(SELECT msg_reply_to FROM msz_messages WHERE msg_id = ?)'
));
if($messageInfoOrId instanceof MessageInfo)
$stmt->addParameter(1, $useReplyTo ? $messageInfoOrId->getReplyToId() : $messageInfoOrId->getId());
else
$stmt->addParameter(1, $messageInfoOrId);
$stmt->addParameter(2, $ownerInfo instanceof UserInfo ? $ownerInfo->getId() : $ownerInfo);
$stmt->execute();
$result = $stmt->getResult();
if(!$result->next())
throw new RuntimeException('Message not found.');
return MessageInfo::fromResult($result);
}
public function createMessage(
string $messageId,
UserInfo|string $ownerInfo,
UserInfo|string|null $authorInfo,
UserInfo|string|null $recipientInfo,
string $title,
string $body,
int $parser,
MessageInfo|string|null $replyTo = null,
DateTime|int|null $sentAt = null,
DateTime|int|null $readAt = null
): MessageInfo {
$stmt = $this->cache->get('INSERT INTO msz_messages (msg_id, msg_owner_id, msg_author_id, msg_recipient_id, msg_reply_to, msg_title, msg_body, msg_parser, msg_sent, msg_read) VALUES (?, ?, ?, ?, ?, ?, ?, ?, FROM_UNIXTIME(?), FROM_UNIXTIME(?))');
$stmt->addParameter(1, $messageId);
$stmt->addParameter(2, $ownerInfo instanceof UserInfo ? $ownerInfo->getId() : $ownerInfo);
$stmt->addParameter(3, $authorInfo instanceof UserInfo ? $authorInfo->getId() : $authorInfo);
$stmt->addParameter(4, $recipientInfo instanceof UserInfo ? $recipientInfo->getId() : $recipientInfo);
$stmt->addParameter(5, $replyTo instanceof MessageInfo ? $replyTo->getId() : $replyTo);
$stmt->addParameter(6, $title);
$stmt->addParameter(7, $body);
$stmt->addParameter(8, $parser);
$stmt->addParameter(9, $sentAt instanceof DateTime ? $sentAt->getUnixTimeSeconds() : $sentAt);
$stmt->addParameter(10, $readAt instanceof DateTime ? $readAt->getUnixTimeSeconds() : $readAt);
$stmt->execute();
return $this->getMessageInfo($ownerInfo, $messageId);
}
public function updateMessage(
UserInfo|string|null $ownerInfo = null,
MessageInfo|string|null $messageInfo = null,
?string $title = null,
?string $body = null,
?int $parser = null,
DateTime|int|null|false $sentAt = false,
DateTime|int|null|false $readAt = false
): void {
$setQuery = [];
$setValues = [];
$whereQuery = [];
$whereValues = [];
if($ownerInfo !== null) {
$whereQuery[] = 'msg_owner_id = ?';
$whereValues[] = $ownerInfo instanceof UserInfo ? $ownerInfo->getId() : $ownerInfo;
}
if($messageInfo !== null) {
$whereQuery[] = 'msg_id = ?';
$whereValues[] = $messageInfo instanceof MessageInfo ? $messageInfo->getId() : $messageInfo;
}
if($title !== null) {
$setQuery[] = 'msg_title = ?';
$setValues[] = $title;
}
if($body !== null) {
$setQuery[] = 'msg_body = ?';
$setValues[] = $body;
}
if($parser !== null) {
$setQuery[] = 'msg_parser = ?';
$setValues[] = $parser;
}
if($sentAt !== false) {
$setQuery[] = 'msg_sent = FROM_UNIXTIME(?)';
$setValues[] = $sentAt instanceof DateTime ? $sentAt->getUnixTimeSeconds() : $sentAt;
}
if($readAt !== false) {
$setQuery[] = 'msg_read = FROM_UNIXTIME(?)';
$setValues[] = $readAt instanceof DateTime ? $readAt->getUnixTimeSeconds() : $readAt;
}
if(empty($whereQuery))
throw new InvalidArgumentException('$ownerInfo or $messageInfo must be specified.');
if(empty($setQuery))
return;
$args = 0;
$stmt = $this->cache->get(sprintf(
'UPDATE msz_messages SET %s WHERE %s',
implode(', ', $setQuery),
implode(' AND ', $whereQuery)
));
foreach($setValues as $value)
$stmt->addParameter(++$args, $value);
foreach($whereValues as $value)
$stmt->addParameter(++$args, $value);
$stmt->execute();
}
public function deleteMessages(
UserInfo|string|null $ownerInfo,
MessageInfo|array|string|null $messageInfos
): void {
$hasOwnerInfo = $ownerInfo !== null;
$hasMessageInfos = $messageInfos !== null;
$query = 'UPDATE msz_messages SET msg_deleted = NOW() WHERE msg_deleted IS NULL';
if($hasOwnerInfo)
$query .= ' AND msg_owner_id = ?';
if($hasMessageInfos) {
if(!is_array($messageInfos))
$messageInfos = [$messageInfos];
$query .= sprintf(
' AND msg_id IN (%s)',
DbTools::prepareListString($messageInfos)
);
}
$args = 0;
$stmt = $this->cache->get($query);
if($hasOwnerInfo)
$stmt->addParameter(++$args, $ownerInfo instanceof UserInfo ? $ownerInfo->getId() : $ownerInfo);
if($hasMessageInfos)
foreach($messageInfos as $messageInfo) {
if(is_string($messageInfo))
$stmt->addParameter(++$args, $messageInfo);
elseif($messageInfo instanceof MessageInfo)
$stmt->addParameter(++$args, $messageInfo->getId());
else
throw new InvalidArgumentException('$messageInfos must be an array of strings or MessageInfo instances.');
}
$stmt->execute();
}
public function restoreMessages(
UserInfo|string|null $ownerInfo,
MessageInfo|array|string|null $messageInfos
): void {
$hasOwnerInfo = $ownerInfo !== null;
$hasMessageInfos = $messageInfos !== null;
$query = 'UPDATE msz_messages SET msg_deleted = NULL WHERE msg_deleted IS NOT NULL';
if($hasOwnerInfo)
$query .= ' AND msg_owner_id = ?';
if($hasMessageInfos) {
if(!is_array($messageInfos))
$messageInfos = [$messageInfos];
$query .= sprintf(
' AND msg_id IN (%s)',
DbTools::prepareListString($messageInfos)
);
}
$args = 0;
$stmt = $this->cache->get($query);
if($hasOwnerInfo)
$stmt->addParameter(++$args, $ownerInfo instanceof UserInfo ? $ownerInfo->getId() : $ownerInfo);
if($hasMessageInfos)
foreach($messageInfos as $messageInfo) {
if(is_string($messageInfo))
$stmt->addParameter(++$args, $messageInfo);
elseif($messageInfo instanceof MessageInfo)
$stmt->addParameter(++$args, $messageInfo->getId());
else
throw new InvalidArgumentException('$messageInfos must be an array of strings or MessageInfo instances.');
}
$stmt->execute();
}
public function nukeMessages(
UserInfo|string|null $ownerInfo,
MessageInfo|array|string|null $messageInfos
): void {
$hasOwnerInfo = $ownerInfo !== null;
$hasMessageInfos = $messageInfos !== null;
$query = 'DELETE FROM msz_messages WHERE msg_deleted IS NOT NULL';
if($hasOwnerInfo)
$query .= ' AND msg_owner_id = ?';
if($hasMessageInfos) {
if(!is_array($messageInfos))
$messageInfos = [$messageInfos];
$query .= sprintf(
' AND msg_id IN (%s)',
DbTools::prepareListString($messageInfos)
);
}
$args = 0;
$stmt = $this->cache->get($query);
if($hasOwnerInfo)
$stmt->addParameter(++$args, $ownerInfo instanceof UserInfo ? $ownerInfo->getId() : $ownerInfo);
if($hasMessageInfos)
foreach($messageInfos as $messageInfo) {
if(is_string($messageInfo))
$stmt->addParameter(++$args, $messageInfo);
elseif($messageInfo instanceof MessageInfo)
$stmt->addParameter(++$args, $messageInfo->getId());
else
throw new InvalidArgumentException('$messageInfos must be an array of strings or MessageInfo instances.');
}
$stmt->execute();
}
}