Modele i baza danych
Warstwa danych TubePress: klasy modeli, pomocnik bazy danych PDO, zapytania przygotowane, schemat bazy danych i wersjonowane migracje SQL — wyjaśnione.
TubePress komunikuje się z MySQL/MariaDB przez dwie cienkie warstwy: modele (jedna klasa statyczna na encję) i helpera Database (mały wrapper nad PDO). Nie ma ORM ani query buildera do nauki — tylko prepared statements i przewidywalne nazwy metod.
Warstwa modeli
Każda główna encja ma model w src/Models/ udostępniający metody statyczne. Dostępne modele to Video, Category, Tag, Performer, Channel, Comment, Page, User, Setting, Favorite, History, Report, ContactMessage, MenuConfig i 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);Modele hermetyzują złączenia i filtry charakterystyczne dla swojej encji — na przykład Video::find() zwraca film razem z dołączonymi kategoriami, tagami, wykonawcami i kanałami, dzięki czemu kontrolery i szablony pozostają proste.
Helper Database
W przypadkach nieobsłużonych przez model użyj helpera Database bezpośrednio. Każda metoda korzysta z prepared statements — nigdy nie łącz danych wejściowych użytkownika z SQL.
| Metoda | Zwraca | Cel |
|---|---|---|
Database::fetchOne($sql, $params) | array\|null | Pierwszy pasujący wiersz. |
Database::fetchAll($sql, $params) | array | Wszystkie pasujące wiersze. |
Database::insert($table, $data) | int | Wstawia tablicę asocjacyjną; zwraca nowe id. |
Database::update($table, $data, $where, $whereParams) | int | Aktualizacja; zwraca liczbę zmienionych wierszy. |
Database::delete($table, $where, $params) | int | Usunięcie; zwraca liczbę zmienionych wierszy. |
Database::count($table, $where, $params) | int | Liczba wierszy. |
Database::query($sql, $params) | PDOStatement | Dowolne polecenie, pełna kontrola. |
Database::transaction($callback) | mixed | Uruchamia domknięcie w transakcji (automatyczny commit/rollback). |
$rows = Database::fetchAll(
"SELECT * FROM videos WHERE status = ? ORDER BY id DESC LIMIT ?",
['published', 20]
);
$newId = Database::insert('categories', [
'name' => 'Amateur',
'slug' => 'amateur',
]);utf8mb4, prawdziwych prepared statements (bez emulacji) i trybu błędów opartego na wyjątkach, a po wykryciu błędu „server has gone away" automatycznie łączy się ponownie — dzięki czemu długo działające workery przeżywają bezczynny timeout MySQL.Transakcje
Opakuj wieloetapowe zapisy, aby wszystkie się powiodły lub wszystkie zostały wycofane:
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,
]);
}
});Schemat bazy danych
Baza danych używa przejrzystego, znormalizowanego schematu MySQL. Tabele główne to users, settings, categories, tags, performers, channels, videos oraz tabele złączeń video_categories, video_tags, video_performers — a także comments, user_favorites, user_history, video_likes, pages, plugins, themes i migrations. Opcjonalne podsystemy dodają własne tabele (zadania importu, zasoby mediów, zadania/serwery transkodowania, strefy/miejsca reklamowe, raporty, źródła feedów, tabele tłumaczeń i inne) w miarę ich włączania.
Migracje
Zmiany schematu przechowywane są w sql/ jako pliki numerowane — 001_users.sql, 002_settings.sql, … — aplikowane kolejno przez Migrator::run() podczas instalacji i po każdej aktualizacji. Każda migracja jest addytywna i rejestrowana w tabeli migrations, dzięki czemu nigdy nie jest uruchamiana dwukrotnie.
- Aby rozszerzyć schemat z poziomu pluginu, dołącz swoje polecenia
CREATE TABLEi uruchom je w metodzieinstall()pluginu (zob. Hooki & pluginy). - Nigdy nie zmieniaj numeracji ani nie edytuj zastosowanej migracji — dodaj nową, z wyższym numerem.
Tworzenie modelu
Model to prosta klasa statyczna opakowująca helpera Database dla jednej tabeli:
class Promo
{
public static function current(): ?array
{
return Database::fetchOne(
"SELECT * FROM promos WHERE active = 1
ORDER BY id DESC LIMIT 1"
);
}
}Następne kroki
Nadal masz problem?
Otwórz zgłoszenie z poziomu panelu, a nasz zespół Ci pomoże.