Modelos y base de datos
La capa de datos de TubePress: las clases de modelo, el asistente de base de datos PDO, las consultas preparadas, el esquema de la base de datos y las migraciones SQL versionadas, explicado todo en detalle.
TubePress se comunica con MySQL/MariaDB a través de dos capas ligeras: los modelos (una clase estática por entidad) y el helper Database (un pequeño envoltorio sobre PDO). No hay ORM ni constructor de consultas que aprender — solo sentencias preparadas y nombres de métodos predecibles.
La capa de modelos
Cada entidad principal tiene un modelo en src/Models/ que expone métodos estáticos. Los modelos disponibles son Video, Category, Tag, Performer, Channel, Comment, Page, User, Setting, Favorite, History, Report, ContactMessage, MenuConfig y 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);Los modelos encapsulan los joins y filtros específicos de su entidad — por ejemplo, Video::find() devuelve el vídeo con sus categorías, etiquetas, intérpretes y canales asociados, de modo que los controladores y las plantillas permanezcan simples.
El helper Database
Para todo lo que un modelo no cubra, usa el helper Database directamente. Cada método utiliza sentencias preparadas — nunca concatenes entradas del usuario en SQL.
| Método | Devuelve | Propósito |
|---|---|---|
Database::fetchOne($sql, $params) | array\|null | Primera fila coincidente. |
Database::fetchAll($sql, $params) | array | Todas las filas coincidentes. |
Database::insert($table, $data) | int | Inserta un array asociativo; devuelve el nuevo id. |
Database::update($table, $data, $where, $whereParams) | int | Actualiza; devuelve las filas afectadas. |
Database::delete($table, $where, $params) | int | Elimina; devuelve las filas afectadas. |
Database::count($table, $where, $params) | int | Conteo de filas. |
Database::query($sql, $params) | PDOStatement | Cualquier sentencia, para control total. |
Database::transaction($callback) | mixed | Ejecuta un cierre en una transacción (commit/rollback automático). |
$rows = Database::fetchAll(
"SELECT * FROM videos WHERE status = ? ORDER BY id DESC LIMIT ?",
['published', 20]
);
$newId = Database::insert('categories', [
'name' => 'Amateur',
'slug' => 'amateur',
]);utf8mb4, sentencias preparadas reales (sin emulación) y modo de error por excepción, y se reconecta automáticamente una vez ante un error genuino de "server has gone away" — de modo que los trabajadores de larga duración sobreviven a un tiempo de espera inactivo de MySQL.Transacciones
Envuelve escrituras de varios pasos para que todas tengan éxito o todas se reviertan:
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,
]);
}
});El esquema
La base de datos utiliza un esquema MySQL limpio y normalizado. Las tablas principales incluyen users, settings, categories, tags, performers, channels, videos y las tablas de unión video_categories, video_tags, video_performers — más comments, user_favorites, user_history, video_likes, pages, plugins, themes y migrations. Los subsistemas opcionales añaden sus propias tablas (trabajos de importación, almacenamientos de medios, trabajos/servidores de transcodificación, zonas/spots publicitarios, informes, fuentes de feeds, tablas de traducción y más) a medida que los activas.
Migraciones
Los cambios de esquema se encuentran en sql/ como archivos numerados — 001_users.sql, 002_settings.sql, … — aplicados en orden por Migrator::run() durante la instalación y después de cada actualización. Cada migración es aditiva y se registra en la tabla migrations para que nunca se ejecute dos veces.
- Para extender el esquema desde un plugin, incluye tus sentencias
CREATE TABLEy ejecútalas en el métodoinstall()del plugin (ver Hooks & plugins). - Nunca renumeres ni edites una migración ya aplicada — añade una nueva con un número mayor.
Escribir un modelo
Un modelo es simplemente una clase estática que envuelve el helper Database para una tabla:
class Promo
{
public static function current(): ?array
{
return Database::fetchOne(
"SELECT * FROM promos WHERE active = 1
ORDER BY id DESC LIMIT 1"
);
}
}Próximos pasos
¿Todavía con dudas?
Abre un ticket desde tu panel y nuestro equipo te ayudará.