Remove all static model methods. (#52)
Replace all static model methods. Support dependency injection for all models. Clean up things that used the static model methods. Decouple ConfigModel and CssModel. Reviewed-on: https://gitea.subcultureofone.org/greg/tkr/pulls/52 Co-authored-by: Greg Sarjeant <greg@subcultureofone.org> Co-committed-by: Greg Sarjeant <greg@subcultureofone.org>
This commit is contained in:
parent
dc4f60ce2e
commit
832b7b95fa
@ -72,8 +72,8 @@ global $app;
|
|||||||
|
|
||||||
$app = [
|
$app = [
|
||||||
'db' => $db,
|
'db' => $db,
|
||||||
'config' => (new ConfigModel($db))->loadFromDatabase(),
|
'config' => (new ConfigModel($db))->get(),
|
||||||
'user' => (new UserModel($db))->loadFromDatabase(),
|
'user' => (new UserModel($db))->get(),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Start a session and generate a CSRF Token
|
// Start a session and generate a CSRF Token
|
||||||
|
@ -13,6 +13,16 @@ class Controller {
|
|||||||
throw new RuntimeException("Template not found: $childTemplatePath");
|
throw new RuntimeException("Template not found: $childTemplatePath");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add custom CSS filename if needed
|
||||||
|
global $app;
|
||||||
|
if ($app['config']->cssId) {
|
||||||
|
$cssModel = new CssModel($app['db']);
|
||||||
|
$cssFile = $cssModel->getById($app['config']->cssId);
|
||||||
|
$vars['customCssFilename'] = $cssFile['filename'] ?? null;
|
||||||
|
} else {
|
||||||
|
$vars['customCssFilename'] = null;
|
||||||
|
}
|
||||||
|
|
||||||
// always check for flash messages and add them if they exist
|
// always check for flash messages and add them if they exist
|
||||||
if (Session::hasFlashMessages()){
|
if (Session::hasFlashMessages()){
|
||||||
$flashMessages = Session::getFlashMessages();
|
$flashMessages = Session::getFlashMessages();
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
class CssController extends Controller {
|
class CssController extends Controller {
|
||||||
public function index() {
|
public function index() {
|
||||||
global $app;
|
global $app;
|
||||||
|
$cssModel = new CssModel($app['db']);
|
||||||
$customCss = CssModel::load();
|
$customCss = $cssModel->getAll();
|
||||||
|
|
||||||
$vars = [
|
$vars = [
|
||||||
'user' => $app['user'],
|
'user' => $app['user'],
|
||||||
@ -16,7 +16,8 @@ class CssController extends Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function serveCustomCss(string $baseFilename){
|
public function serveCustomCss(string $baseFilename){
|
||||||
$cssModel = new CssModel();
|
global $app;
|
||||||
|
$cssModel = new CssModel($app['db']);
|
||||||
$filename = "$baseFilename.css";
|
$filename = "$baseFilename.css";
|
||||||
|
|
||||||
$cssRow = $cssModel->getByFilename($filename);
|
$cssRow = $cssModel->getByFilename($filename);
|
||||||
@ -77,7 +78,7 @@ class CssController extends Controller {
|
|||||||
|
|
||||||
// Get the data for the selected CSS file
|
// Get the data for the selected CSS file
|
||||||
$cssId = $_POST['selectCssFile'];
|
$cssId = $_POST['selectCssFile'];
|
||||||
$cssModel = new CssModel();
|
$cssModel = new CssModel($app['db']);
|
||||||
$cssRow = $cssModel->getById($cssId);
|
$cssRow = $cssModel->getById($cssId);
|
||||||
|
|
||||||
// exit if the requested file isn't in the database
|
// exit if the requested file isn't in the database
|
||||||
@ -182,7 +183,8 @@ class CssController extends Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add upload to database
|
// Add upload to database
|
||||||
$cssModel = new CssModel();
|
global $app;
|
||||||
|
$cssModel = new CssModel($app['db']);
|
||||||
$cssModel->save($safeFilename, $description);
|
$cssModel->save($safeFilename, $description);
|
||||||
|
|
||||||
// Set success flash message
|
// Set success flash message
|
||||||
|
@ -3,8 +3,9 @@
|
|||||||
// Shows the custom emoji management page
|
// Shows the custom emoji management page
|
||||||
public function index(){
|
public function index(){
|
||||||
global $app;
|
global $app;
|
||||||
|
|
||||||
$emojiList = EmojiModel::loadAll();
|
$emojiModel = new EmojiModel($app['db']);
|
||||||
|
$emojiList = $emojiModel->getAll();
|
||||||
|
|
||||||
$vars = [
|
$vars = [
|
||||||
'config' => $app['config'],
|
'config' => $app['config'],
|
||||||
@ -34,14 +35,14 @@
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleAdd(string $emoji, ?string $description=null): void {
|
private function isValidEmoji(string $emoji){
|
||||||
// Validate 1 visible character in the emoji
|
// Validate 1 visible character in the emoji
|
||||||
if (extension_loaded('mbstring')) {
|
if (extension_loaded('mbstring')) {
|
||||||
// TODO - log a warning if mbstring isn't loaded
|
// TODO - log a warning if mbstring isn't loaded
|
||||||
$charCount = mb_strlen($emoji, 'UTF-8');
|
$charCount = mb_strlen($emoji, 'UTF-8');
|
||||||
if ($charCount !== 1) {
|
if ($charCount !== 1) {
|
||||||
// TODO - handle error
|
// TODO - handle error
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,25 +51,40 @@
|
|||||||
|
|
||||||
if (!preg_match($emojiPattern, $emoji)) {
|
if (!preg_match($emojiPattern, $emoji)) {
|
||||||
// TODO - handle error
|
// TODO - handle error
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// emojis should have more bytes than characters
|
// emojis should have more bytes than characters
|
||||||
$byteCount = strlen($emoji);
|
$byteCount = strlen($emoji);
|
||||||
if ($byteCount <= 1) {
|
if ($byteCount <= 1) {
|
||||||
// TODO - handle error
|
// TODO - handle error
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleAdd(string $emoji, ?string $description=null): void {
|
||||||
|
global $app;
|
||||||
|
|
||||||
|
if (!$this->isValidEmoji($emoji)){
|
||||||
|
// TODO - handle
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// It looks like an emoji. Let's add it.
|
// It looks like an emoji. Let's add it.
|
||||||
EmojiModel::add($emoji, $description);
|
$emojiModel = new EmojiModel($app['db']);
|
||||||
|
$emojiList = $emojiModel->add($emoji, $description);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleDelete(): void {
|
public function handleDelete(): void {
|
||||||
|
global $app;
|
||||||
|
|
||||||
$ids = $_POST['delete_emoji_ids'];
|
$ids = $_POST['delete_emoji_ids'];
|
||||||
|
|
||||||
if (!empty($ids)) {
|
if (!empty($ids)) {
|
||||||
EmojiModel::delete($ids);
|
$emojiModel = new EmojiModel($app['db']);
|
||||||
|
$emojiModel->delete($ids);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,7 +2,7 @@
|
|||||||
class MoodController extends Controller {
|
class MoodController extends Controller {
|
||||||
public function index(){
|
public function index(){
|
||||||
global $app;
|
global $app;
|
||||||
|
|
||||||
$view = new MoodView();
|
$view = new MoodView();
|
||||||
|
|
||||||
$moodPicker = $view->render_mood_picker(self::getEmojisWithLabels(), $app['user']->mood);
|
$moodPicker = $view->render_mood_picker(self::getEmojisWithLabels(), $app['user']->mood);
|
||||||
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
public function handlePost(){
|
public function handlePost(){
|
||||||
global $app;
|
global $app;
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
switch ($_POST['action']){
|
switch ($_POST['action']){
|
||||||
case 'set':
|
case 'set':
|
||||||
@ -39,7 +39,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static function getEmojisWithLabels(): array {
|
private static function getEmojisWithLabels(): array {
|
||||||
$customEmoji = EmojiModel::loadAll();
|
global $app;
|
||||||
|
|
||||||
|
$emojiModel = new EmojiModel($app['db']);
|
||||||
|
$customEmoji = $emojiModel->getAll();
|
||||||
|
|
||||||
if (!empty($customEmoji)){
|
if (!empty($customEmoji)){
|
||||||
$custom = [];
|
$custom = [];
|
||||||
|
@ -13,15 +13,8 @@ class ConfigModel {
|
|||||||
|
|
||||||
public function __construct(private PDO $db) {}
|
public function __construct(private PDO $db) {}
|
||||||
|
|
||||||
// load config from sqlite database (backward compatibility)
|
|
||||||
public static function load(): self {
|
|
||||||
global $db;
|
|
||||||
$instance = new self($db);
|
|
||||||
return $instance->loadFromDatabase();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instance method that uses injected database
|
// Instance method that uses injected database
|
||||||
public function loadFromDatabase(): self {
|
public function get(): self {
|
||||||
$init = require APP_ROOT . '/config/init.php';
|
$init = require APP_ROOT . '/config/init.php';
|
||||||
$c = new self($this->db);
|
$c = new self($this->db);
|
||||||
$c->baseUrl = ($c->baseUrl === '') ? $init['base_url'] : $c->baseUrl;
|
$c->baseUrl = ($c->baseUrl === '') ? $init['base_url'] : $c->baseUrl;
|
||||||
@ -53,18 +46,6 @@ class ConfigModel {
|
|||||||
return $c;
|
return $c;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function customCssFilename() {
|
|
||||||
if (empty($this->cssId)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch filename from css table using cssId
|
|
||||||
$cssModel = new CssModel();
|
|
||||||
$cssRecord = $cssModel->getById($this->cssId);
|
|
||||||
|
|
||||||
return $cssRecord ? $cssRecord['filename'] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function save(): self {
|
public function save(): self {
|
||||||
$settingsCount = (int) $this->db->query("SELECT COUNT(*) FROM settings")->fetchColumn();
|
$settingsCount = (int) $this->db->query("SELECT COUNT(*) FROM settings")->fetchColumn();
|
||||||
|
|
||||||
@ -104,6 +85,6 @@ class ConfigModel {
|
|||||||
$this->logLevel
|
$this->logLevel
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $this->loadFromDatabase();
|
return $this->get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,44 +1,41 @@
|
|||||||
<?php
|
<?php
|
||||||
class CssModel {
|
class CssModel {
|
||||||
public static function load(): Array {
|
public function __construct(private PDO $db) {}
|
||||||
global $db;
|
|
||||||
$stmt = $db->prepare("SELECT id, filename, description FROM css ORDER BY filename");
|
public function getAll(): Array {
|
||||||
|
$stmt = $this->db->prepare("SELECT id, filename, description FROM css ORDER BY filename");
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
|
|
||||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function getById(int $id): Array{
|
public function getById(int $id): Array{
|
||||||
global $db;
|
$stmt = $this->db->prepare("SELECT id, filename, description FROM css WHERE id=?");
|
||||||
$stmt = $db->prepare("SELECT id, filename, description FROM css WHERE id=?");
|
|
||||||
$stmt->execute([$id]);
|
$stmt->execute([$id]);
|
||||||
return $stmt->fetch(PDO::FETCH_ASSOC);
|
return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getByFilename(string $filename): Array{
|
public function getByFilename(string $filename): Array{
|
||||||
global $db;
|
$stmt = $this->db->prepare("SELECT id, filename, description FROM css WHERE filename=?");
|
||||||
$stmt = $db->prepare("SELECT id, filename, description FROM css WHERE filename=?");
|
|
||||||
$stmt->execute([$filename]);
|
$stmt->execute([$filename]);
|
||||||
return $stmt->fetch(PDO::FETCH_ASSOC);
|
return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete(int $id): bool{
|
public function delete(int $id): bool{
|
||||||
global $db;
|
$stmt = $this->db->prepare("DELETE FROM css WHERE id=?");
|
||||||
$stmt = $db->prepare("DELETE FROM css WHERE id=?");
|
|
||||||
return $stmt->execute([$id]);
|
return $stmt->execute([$id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function save(string $filename, ?string $description = null): void {
|
public function save(string $filename, ?string $description = null): void {
|
||||||
global $db;
|
$stmt = $this->db->prepare("SELECT COUNT(id) FROM css WHERE filename = ?");
|
||||||
|
|
||||||
$stmt = $db->prepare("SELECT COUNT(id) FROM css WHERE filename = ?");
|
|
||||||
$stmt->execute([$filename]);
|
$stmt->execute([$filename]);
|
||||||
$fileExists = $stmt->fetchColumn();
|
$fileExists = $stmt->fetchColumn();
|
||||||
|
|
||||||
if ($fileExists) {
|
if ($fileExists) {
|
||||||
$stmt = $db->prepare("UPDATE css SET description = ? WHERE filename = ?");
|
$stmt = $this->db->prepare("UPDATE css SET description = ? WHERE filename = ?");
|
||||||
} else {
|
} else {
|
||||||
$stmt = $db->prepare("INSERT INTO css (filename, description) VALUES (?, ?)");
|
$stmt = $this->db->prepare("INSERT INTO css (filename, description) VALUES (?, ?)");
|
||||||
}
|
}
|
||||||
|
|
||||||
$stmt->execute([$filename, $description]);
|
$stmt->execute([$filename, $description]);
|
||||||
|
@ -1,29 +1,25 @@
|
|||||||
<?php
|
<?php
|
||||||
// welp this model is overkill
|
// welp this model is overkill
|
||||||
class EmojiModel{
|
class EmojiModel{
|
||||||
|
public function __construct(private PDO $db) {}
|
||||||
|
|
||||||
// This isn't memory-efficient,
|
// This isn't memory-efficient,
|
||||||
// but I think it'll be fine on this app's scales.
|
// but I think it'll be fine on this app's scales.
|
||||||
public static function loadAll(): array {
|
public function getAll(): array {
|
||||||
global $db;
|
$stmt = $this->db->query("SELECT id, emoji, description FROM emoji");
|
||||||
|
|
||||||
$stmt = $db->query("SELECT id, emoji, description FROM emoji");
|
|
||||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
// I'm not going to support editing emoji.
|
// I'm not going to support editing emoji.
|
||||||
// It'll just be a delete/readd
|
// It'll just be a delete/readd
|
||||||
public static function add(string $emoji, ?string $description): void{
|
public function add(string $emoji, ?string $description): void{
|
||||||
global $db;
|
$stmt = $this->db->prepare("INSERT INTO emoji (emoji, description) VALUES (?, ?)");
|
||||||
|
|
||||||
$stmt = $db->prepare("INSERT INTO emoji (emoji, description) VALUES (?, ?)");
|
|
||||||
$stmt->execute([$emoji, $description]);
|
$stmt->execute([$emoji, $description]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function delete(array $idsToDelete): void{
|
public function delete(array $idsToDelete): void{
|
||||||
global $db;
|
|
||||||
|
|
||||||
$placeholders = rtrim(str_repeat('?,', count($idsToDelete)), ',');
|
$placeholders = rtrim(str_repeat('?,', count($idsToDelete)), ',');
|
||||||
$stmt = $db->prepare("DELETE FROM emoji WHERE id IN ($placeholders)");
|
$stmt = $this->db->prepare("DELETE FROM emoji WHERE id IN ($placeholders)");
|
||||||
$stmt->execute($idsToDelete);
|
$stmt->execute($idsToDelete);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -8,15 +8,7 @@ class UserModel {
|
|||||||
|
|
||||||
public function __construct(private PDO $db) {}
|
public function __construct(private PDO $db) {}
|
||||||
|
|
||||||
// load user settings from sqlite database (backward compatibility)
|
public function get(): self {
|
||||||
public static function load(): self {
|
|
||||||
global $db;
|
|
||||||
$instance = new self($db);
|
|
||||||
return $instance->loadFromDatabase();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instance method that uses injected database
|
|
||||||
public function loadFromDatabase(): self {
|
|
||||||
// There's only ever one user. I'm just leaning into that.
|
// There's only ever one user. I'm just leaning into that.
|
||||||
$stmt = $this->db->query("SELECT username, display_name, website, mood FROM user WHERE id=1");
|
$stmt = $this->db->query("SELECT username, display_name, website, mood FROM user WHERE id=1");
|
||||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
@ -43,7 +35,7 @@ class UserModel {
|
|||||||
|
|
||||||
$stmt->execute([$this->username, $this->displayName, $this->website, $this->mood]);
|
$stmt->execute([$this->username, $this->displayName, $this->website, $this->mood]);
|
||||||
|
|
||||||
return $this->loadFromDatabase();
|
return $this->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Making this a separate function to avoid
|
// Making this a separate function to avoid
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<?php /** @var ConfigModel $config */ ?>
|
<?php /** @var ConfigModel $config */ ?>
|
||||||
<?php /** @var UserModel $user */ ?>
|
<?php /** @var UserModel $user */ ?>
|
||||||
<?php /** @var string $childTemplateFile */ ?>
|
<?php /** @var string $childTemplateFile */ ?>
|
||||||
|
<?php /** @var string $customCssFilename */ ?>
|
||||||
<?php /** @var srting $flashSection */ ?>
|
<?php /** @var srting $flashSection */ ?>
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
@ -13,7 +14,7 @@
|
|||||||
href="<?= Util::escape_html(Util::buildRelativeUrl($config->basePath, 'css/default.css')) ?>">
|
href="<?= Util::escape_html(Util::buildRelativeUrl($config->basePath, 'css/default.css')) ?>">
|
||||||
<?php if (!empty($config->cssId)): ?>
|
<?php if (!empty($config->cssId)): ?>
|
||||||
<link rel="stylesheet"
|
<link rel="stylesheet"
|
||||||
href="<?= Util::escape_html(Util::buildRelativeUrl($config->basePath, 'css/custom/' . $config->customCssFilename())) ?>">
|
href="<?= Util::escape_html(Util::buildRelativeUrl($config->basePath, 'css/custom/' . $customCssFilename)) ?>">
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<link rel="alternate"
|
<link rel="alternate"
|
||||||
type="application/rss+xml"
|
type="application/rss+xml"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user