Models e banco de dados
A camada de dados do TubePress: classes de model, o helper Database do PDO, prepared statements, o esquema do banco de dados e as migrações SQL versionadas explicadas.
TubePress se comunica com MySQL/MariaDB por meio de duas camadas simples: modelos (uma classe estática por entidade) e o helper Database (um pequeno wrapper sobre PDO). Não há ORM nem query builder para aprender — apenas prepared statements e nomes de métodos previsíveis.
A camada de modelos
Cada entidade principal possui um modelo em src/Models/ que expõe métodos estáticos. Os modelos disponíveis são Video, Category, Tag, Performer, Channel, Comment, Page, User, Setting, Favorite, History, Report, ContactMessage, MenuConfig e 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);Os modelos encapsulam os joins e filtros específicos de cada entidade — por exemplo, Video::find() retorna o vídeo com suas categorias, tags, performers e canais associados, mantendo os controllers e templates simples.
O helper Database
Para qualquer coisa que um modelo não cubra, use o helper Database diretamente. Todos os métodos utilizam prepared statements — nunca concatene entradas do usuário em SQL.
| Método | Retorna | Finalidade |
|---|---|---|
Database::fetchOne($sql, $params) | array\|null | Primeira linha correspondente. |
Database::fetchAll($sql, $params) | array | Todas as linhas correspondentes. |
Database::insert($table, $data) | int | Insere um array associativo; retorna o novo id. |
Database::update($table, $data, $where, $whereParams) | int | Atualiza; retorna as linhas afetadas. |
Database::delete($table, $where, $params) | int | Exclui; retorna as linhas afetadas. |
Database::count($table, $where, $params) | int | Contagem de linhas. |
Database::query($sql, $params) | PDOStatement | Qualquer instrução, para controle total. |
Database::transaction($callback) | mixed | Executa uma closure em uma transação (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, prepared statements reais (sem emulação) e modo de erro por exceção, e reconecta automaticamente uma vez em caso de erro genuíno de "server has gone away" — assim, workers de longa duração sobrevivem a um timeout ocioso do MySQL.Transações
Agrupe escritas em múltiplas etapas para que todas sejam bem-sucedidas ou revertidas:
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,
]);
}
});O esquema
O banco de dados utiliza um esquema MySQL limpo e normalizado. As tabelas principais incluem users, settings, categories, tags, performers, channels, videos e as tabelas de junção video_categories, video_tags, video_performers — além de comments, user_favorites, user_history, video_likes, pages, plugins, themes e migrations. Subsistemas opcionais adicionam suas próprias tabelas (jobs de importação, armazenamentos de mídia, jobs/servidores de transcodificação, zonas/spots de anúncios, relatórios, fontes de feed, tabelas de tradução e mais) conforme são habilitados.
Migrações
As alterações de esquema ficam em sql/ como arquivos numerados — 001_users.sql, 002_settings.sql, … — aplicados em ordem por Migrator::run() durante a instalação e após cada atualização. Cada migração é aditiva e registrada na tabela migrations, portanto nunca é executada duas vezes.
- Para estender o esquema a partir de um plugin, inclua suas instruções
CREATE TABLEe execute-as no métodoinstall()do plugin (consulte Hooks & plugins). - Nunca renumere ou edite uma migração já aplicada — adicione uma nova com número maior.
Criando um modelo
Um modelo é apenas uma classe estática que encapsula o helper Database para uma tabela:
class Promo
{
public static function current(): ?array
{
return Database::fetchOne(
"SELECT * FROM promos WHERE active = 1
ORDER BY id DESC LIMIT 1"
);
}
}Próximos passos
Ainda com dúvidas?
Abra um ticket pelo painel e nossa equipe vai ajudá-lo.