模型与数据库
TubePress 数据层详解:模型类、PDO Database 辅助工具、预处理语句、数据库结构以及版本化 SQL 迁移。
TubePress 通过两个精简层与 MySQL/MariaDB 交互:模型(每个实体一个静态类)和 Database 辅助类(PDO 的轻量封装)。无需学习 ORM 或查询构建器——只需使用预处理语句和规律的方法命名。
模型层
每个核心实体在 src/Models/ 下都有一个暴露静态方法的模型。可用的模型包括 Video、Category、Tag、Performer、Channel、Comment、Page、User、Setting、Favorite、History、Report、ContactMessage、MenuConfig 和 FooterConfig。
$video = Video::find($id); // one row (or null)
$latest = Video::all(['status' => 'published', 'limit' => 20]);
$id = Video::create($data); // returns new id
Video::update($id, ['title' => 'New title']);
Video::delete($id);模型封装了各实体特有的连接和过滤逻辑——例如 Video::find() 会返回视频及其关联的分类、标签、演员和频道,从而使控制器和模板保持简洁。
Database 辅助类
对于模型尚未涵盖的操作,可直接使用 Database 辅助类。所有方法均使用预处理语句——绝不将用户输入拼接到 SQL 中。
| 方法 | 返回值 | 用途 |
|---|---|---|
Database::fetchOne($sql, $params) | array\|null | 第一条匹配行。 |
Database::fetchAll($sql, $params) | array | 所有匹配行。 |
Database::insert($table, $data) | int | 插入关联数组,返回新 id。 |
Database::update($table, $data, $where, $whereParams) | int | 更新,返回受影响行数。 |
Database::delete($table, $where, $params) | int | 删除,返回受影响行数。 |
Database::count($table, $where, $params) | int | 行数统计。 |
Database::query($sql, $params) | PDOStatement | 任意语句,完全可控。 |
Database::transaction($callback) | mixed | 在事务中执行闭包(自动提交/回滚)。 |
$rows = Database::fetchAll(
"SELECT * FROM videos WHERE status = ? ORDER BY id DESC LIMIT ?",
['published', 20]
);
$newId = Database::insert('categories', [
'name' => 'Amateur',
'slug' => 'amateur',
]);utf8mb4、真实预处理语句(非模拟)及异常错误模式,并会在真正的 "server has gone away" 错误发生时自动重连一次——让长期运行的进程在 MySQL 空闲超时后依然能够存活。事务
将多步写入操作包裹在事务中,确保全部成功或全部回滚:
Database::transaction(function () use ($videoId, $catIds) {
Database::delete('video_categories', 'video_id = ?', [$videoId]);
foreach ($catIds as $cid) {
Database::insert('video_categories', [
'video_id' => $videoId, 'category_id' => $cid,
]);
}
});数据库结构
数据库采用规范化的 MySQL 结构。核心表包括 users、settings、categories、tags、performers、channels、videos 以及关联表 video_categories、video_tags、video_performers,此外还有 comments、user_favorites、user_history、video_likes、pages、plugins、themes 和 migrations。启用可选子系统后,它们会添加各自的表(导入任务、媒体存储、转码任务/服务器、广告区域/位置、举报、Feed 源、翻译表等)。
数据库迁移
数据库结构变更以编号文件的形式存放在 sql/ 目录下——001_users.sql、002_settings.sql……在安装时及每次更新后,由 Migrator::run() 按顺序执行。每次迁移均为追加式变更,并记录在 migrations 表中,确保不会重复执行。
- 如需从插件扩展数据库结构,请将
CREATE TABLE语句放入插件的install()方法中执行(参见 Hooks & plugins)。 - 切勿对已执行的迁移进行重新编号或修改——请添加编号更大的新迁移。
编写模型
模型本质上是一个静态类,用于封装针对单张表的 Database 辅助调用:
class Promo
{
public static function current(): ?array
{
return Database::fetchOne(
"SELECT * FROM promos WHERE active = 1
ORDER BY id DESC LIMIT 1"
);
}
}