Added kaomoji list to the database.

This commit is contained in:
flash 2025-04-21 01:25:02 +00:00
parent 45635ddc5b
commit 1d57fc3b45
Signed by: flash
GPG key ID: 2C9C2C574D47FE3E
6 changed files with 221 additions and 0 deletions

View file

@ -0,0 +1,15 @@
<?php
use Index\Db\DbConnection;
use Index\Db\Migration\DbMigration;
final class KaomojiTable_20250421_005231 implements DbMigration {
public function migrate(DbConnection $conn): void {
$conn->execute(<<<SQL
CREATE TABLE msz_kaomoji (
kao_id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
kao_string VARCHAR(255) NOT NULL COLLATE 'utf8mb4_bin',
PRIMARY KEY (kao_id)
) COLLATE='utf8mb4_bin' ENGINE=InnoDB
SQL);
}
}

View file

@ -0,0 +1,76 @@
<?php
namespace Misuzu\Kaomoji;
use RuntimeException;
use Index\XArray;
use Index\Http\{HttpRequest,HttpResponseBuilder};
use Index\Http\Routing\AccessControl\AccessControl;
use Index\Http\Routing\Routes\{ExactRoute,PatternRoute};
use Misuzu\FieldTransformer;
use Misuzu\Routing\{HandlerRoles,RouteHandler,RouteHandlerCommon};
#[HandlerRoles('main')]
final class KaomojiApiRoutes implements RouteHandler {
use RouteHandlerCommon;
public function __construct(
private KaomojiContext $kaomojiCtx
) {}
/** @return FieldTransformer<KaomojiInfo> */
private function createKaomojiTransformer(): FieldTransformer {
return new FieldTransformer([
'id' => [
'transform' => fn($kaomoji) => $kaomoji->id,
],
'string' => [
'default' => true,
'transform' => fn($kaomoji) => $kaomoji->string,
],
]);
}
/** @return int|mixed[] */
#[AccessControl]
#[ExactRoute('GET', '/api/v1/kaomoji')]
public function getEmotes(HttpResponseBuilder $response, HttpRequest $request): array|int {
$kaomoji = $this->kaomojiCtx->kaomoji->getAllKaomoji();
if($request->hasParam('as')) {
if($request->getParam('as') === 'array') {
$response->setCacheControl('max-age=3600', 'stale-if-error=86400', 'public', 'immutable');
return XArray::select($kaomoji, fn($kaomoji) => (string)$kaomoji);
} else return 400;
}
$transformer = $this->createKaomojiTransformer();
if(!$transformer->filter($request))
return 400;
$response->setCacheControl('max-age=3600', 'stale-if-error=86400', 'public', 'immutable');
return XArray::select($kaomoji, fn($kaomoji) => $transformer->convert($kaomoji));
}
/** @return int|mixed[] */
#[AccessControl]
#[PatternRoute('GET', '/api/v1/kaomoji/([0-9]+)')]
public function getEmote(HttpResponseBuilder $response, HttpRequest $request, string $id): array|int {
if(empty($id))
return 404;
$transformer = $this->createKaomojiTransformer();
if(!$transformer->filter($request))
return 400;
try {
$kaomoji = $this->kaomojiCtx->kaomoji->getKaomoji($id);
} catch(RuntimeException $ex) {
return 404;
}
$response->setCacheControl('max-age=3600', 'stale-if-error=86400', 'public', 'immutable');
return $transformer->convert($kaomoji);
}
}

View file

@ -0,0 +1,12 @@
<?php
namespace Misuzu\Kaomoji;
use Index\Db\DbConnection;
class KaomojiContext {
public private(set) KaomojiData $kaomoji;
public function __construct(DbConnection $dbConn) {
$this->kaomoji = new KaomojiData($dbConn);
}
}

View file

@ -0,0 +1,92 @@
<?php
namespace Misuzu\Kaomoji;
use RuntimeException;
use Index\Db\{DbConnection,DbStatementCache};
class KaomojiData {
private DbStatementCache $cache;
public function __construct(DbConnection $dbConn) {
$this->cache = new DbStatementCache($dbConn);
}
public function getKaomoji(string $kaoId): KaomojiInfo {
$stmt = $this->cache->get(<<<SQL
SELECT kao_id, kao_string
FROM msz_kaomoji
WHERE kao_id = ?
SQL);
$stmt->nextParameter($kaoId);
$stmt->execute();
$result = $stmt->getResult();
if(!$result->next())
throw new RuntimeException('No kaomoji with that ID exists.');
return KaomojiInfo::fromResult($result);
}
/**
* @todo pagination
* @return \Iterator<int, KaomojiInfo>
*/
public function getAllKaomoji(): iterable {
$stmt = $this->cache->get(<<<SQL
SELECT kao_id, kao_string
FROM msz_kaomoji
SQL);
$stmt->execute();
return $stmt->getResultIterator(KaomojiInfo::fromResult(...));
}
public function createKaomoji(string $string): KaomojiInfo {
$stmt = $this->cache->get(<<<SQL
INSERT INTO msz_kaomoji (kao_string) VALUES (?)
SQL);
$stmt->nextParameter($string);
$stmt->execute();
return $this->getKaomoji((string)$stmt->lastInsertId);
}
public function deleteKaomoji(KaomojiInfo|string $infoOrId): void {
if($infoOrId instanceof KaomojiInfo)
$infoOrId = $infoOrId->id;
$stmt = $this->cache->get(<<<SQL
DELETE FROM msz_kaomoji
WHERE kao_id = ?
SQL);
$stmt->nextParameter($infoOrId);
$stmt->execute();
}
public function updateKaomoji(
KaomojiInfo|string $infoOrId,
?string $string = null
): void {
$fields = [];
$values = [];
if($string !== null) {
$fields[] = 'kao_string = ?';
$values[] = $string;
}
if(empty($fields))
return;
$fields = implode(', ', $fields);
$stmt = $this->cache->get(<<<SQL
UPDATE msz_kaomoji
SET {$fields}
WHERE kao_id = ?
SQL);
foreach($values as $value)
$stmt->nextParameter($value);
$stmt->nextParameter($infoOrId instanceof KaomojiInfo ? $infoOrId->id : $infoOrId);
$stmt->execute();
}
}

View file

@ -0,0 +1,23 @@
<?php
namespace Misuzu\Kaomoji;
use Stringable;
use Index\Db\DbResult;
class KaomojiInfo implements Stringable {
public function __construct(
public private(set) string $id,
public private(set) string $string,
) {}
public static function fromResult(DbResult $result): KaomojiInfo {
return new KaomojiInfo(
id: $result->getString(0),
string: $result->getString(1),
);
}
public function __toString(): string {
return $this->string;
}
}

View file

@ -42,6 +42,7 @@ class MisuzuContext implements RouteHandler {
public private(set) Comments\CommentsContext $commentsCtx;
public private(set) Emoticons\EmotesContext $emotesCtx;
public private(set) Forum\ForumContext $forumCtx;
public private(set) Kaomoji\KaomojiContext $kaomojiCtx;
public private(set) Logs\LogsContext $logsCtx;
public private(set) Messages\MessagesContext $messagesCtx;
public private(set) OAuth2\OAuth2Context $oauth2Ctx;
@ -105,6 +106,7 @@ class MisuzuContext implements RouteHandler {
Forum\ForumContext::class,
config: $this->config->scopeTo('forum'),
));
$this->deps->register($this->kaomojiCtx = $this->deps->constructLazy(Kaomoji\KaomojiContext::class));
$this->deps->register($this->logsCtx = $this->deps->constructLazy(Logs\LogsContext::class));
$this->deps->register($this->messagesCtx = $this->deps->constructLazy(
Messages\MessagesContext::class,
@ -185,6 +187,7 @@ class MisuzuContext implements RouteHandler {
$this->routingCtx->register($this->deps->constructLazy(Colours\ColoursApiRoutes::class), $roles);
$this->routingCtx->register($this->deps->constructLazy(Emoticons\EmotesApiRoutes::class), $roles);
$this->routingCtx->register($this->deps->constructLazy(Kaomoji\KaomojiApiRoutes::class), $roles);
$this->routingCtx->register($this->deps->constructLazy(Users\UsersApiRoutes::class), $roles);
$this->routingCtx->register($this->deps->constructLazy(Home\HomeRoutes::class), $roles);