Files
MediaCollectorLibary/app/Models/TvShow.php
Lars Behrends e78c073f21 basic filter D:
2025-10-24 17:12:36 +02:00

328 lines
9.6 KiB
PHP

<?php
namespace App\Models;
class TvShow extends Model
{
protected string $table = 'tv_shows';
protected array $fillable = [
'title',
'overview',
'creator',
'genre',
'cast',
'first_air_date',
'last_air_date',
'number_of_seasons',
'number_of_episodes',
'rating',
'imdb_id',
'tmdb_id',
'tvdb_id',
'poster_url',
'backdrop_url',
'is_favorite',
'metadata',
'source_id'
];
protected array $casts = [
'number_of_seasons' => 'int',
'number_of_episodes' => 'int',
'rating' => 'float',
'is_favorite' => 'bool',
'first_air_date' => 'date',
'last_air_date' => 'date',
'metadata' => 'array'
];
/**
* Remove an actor from this TV show
*/
public function removeActor(int $actorId): bool
{
$stmt = $this->pdo->prepare("
DELETE FROM actor_tv_show
WHERE tv_show_id = :tv_show_id AND actor_id = :actor_id
");
return $stmt->execute([
'tv_show_id' => $this->id,
'actor_id' => $actorId
]);
}
/**
* Update the cast field with actor names
*/
public function updateCastField(): bool
{
$actors = $this->actors();
$actorNames = array_column($actors, 'name');
$castString = implode(', ', $actorNames);
return $this->update($this->id, [
'cast' => $castString
]);
}
/**
* Get TV show statistics
*/
public static function getStats(\PDO $pdo): array
{
$stmt = $pdo->query("
SELECT
COUNT(*) as total_tv_shows,
COUNT(CASE WHEN is_favorite = 1 THEN 1 END) as favorite_tv_shows,
AVG(rating) as avg_rating
FROM tv_shows
");
return $stmt->fetch(\PDO::FETCH_ASSOC);
}
/**
* Get total count with optional search
*/
public static function getTotalCount(\PDO $pdo, string $search = '', array $genres = [], array $years = []): int
{
$sql = "SELECT COUNT(*) as count FROM tv_shows t JOIN sources s ON t.source_id = s.id";
$params = [];
if (!empty($search)) {
$sql .= " WHERE t.title LIKE :search";
$params['search'] = "%{$search}%";
}
if (!empty($genres)) {
$placeholders = [];
foreach ($genres as $index => $genre) {
$placeholders[] = ":genre_{$index}";
$params["genre_{$index}"] = $genre;
}
$whereClause = !empty($search) ? " AND" : " WHERE";
$sql .= $whereClause . " t.genre IN (" . implode(',', $placeholders) . ")";
}
if (!empty($years)) {
$placeholders = [];
foreach ($years as $index => $year) {
$placeholders[] = ":year_{$index}";
$params["year_{$index}"] = $year;
}
$whereClause = (!empty($search) || !empty($genres)) ? " AND" : " WHERE";
$sql .= $whereClause . " YEAR(first_air_date) IN (" . implode(',', $placeholders) . ")";
}
$stmt = $pdo->prepare($sql);
foreach ($params as $key => $value) {
$stmt->bindValue(":{$key}", $value);
}
$stmt->execute();
return (int) $stmt->fetch()['count'];
}
/**
* Get all TV shows with pagination and optional search
*/
public static function getAllWithPagination(\PDO $pdo, int $page, int $perPage, string $search = '', array $genres = [], array $years = []): array
{
$offset = ($page - 1) * $perPage;
$sql = "
SELECT t.*, s.display_name as source_name
FROM tv_shows t
JOIN sources s ON t.source_id = s.id
";
$params = [];
if (!empty($search)) {
$sql .= " WHERE t.title LIKE :search";
$params['search'] = "%{$search}%";
}
if (!empty($genres)) {
$placeholders = [];
foreach ($genres as $index => $genre) {
$placeholders[] = ":genre_{$index}";
$params["genre_{$index}"] = $genre;
}
$whereClause = !empty($search) ? " AND" : " WHERE";
$sql .= $whereClause . " t.genre IN (" . implode(',', $placeholders) . ")";
}
if (!empty($years)) {
$placeholders = [];
foreach ($years as $index => $year) {
$placeholders[] = ":year_{$index}";
$params["year_{$index}"] = $year;
}
$whereClause = (!empty($search) || !empty($genres)) ? " AND" : " WHERE";
$sql .= $whereClause . " YEAR(first_air_date) IN (" . implode(',', $placeholders) . ")";
}
$sql .= " ORDER BY t.title ASC LIMIT :limit OFFSET :offset";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':limit', $perPage, \PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, \PDO::PARAM_INT);
foreach ($params as $key => $value) {
$stmt->bindValue(":{$key}", $value);
}
$stmt->execute();
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
}
/**
* Toggle favorite status
*/
public function toggleFavorite(): bool
{
return $this->update($this->id, [
'is_favorite' => !$this->is_favorite
]);
}
/**
* Update rating
*/
public function updateRating(float $rating): bool
{
return $this->update($this->id, [
'rating' => min(10.0, max(0.0, $rating))
]);
}
/**
* Get the source relationship
*/
public function source(): ?Source
{
$stmt = $this->pdo->prepare("SELECT * FROM sources WHERE id = :source_id");
$stmt->execute(['source_id' => $this->source_id]);
$sourceData = $stmt->fetch(\PDO::FETCH_ASSOC);
return $sourceData ? new Source($this->pdo, $sourceData) : null;
}
public function getSeasonsWithEpisodes(): array
{
// Get all episodes for this TV show, grouped by season
$stmt = $this->pdo->prepare("
SELECT season_number,
COUNT(*) as episode_count,
SUM(CASE WHEN watched = 1 THEN 1 ELSE 0 END) as watched_episodes
FROM tv_episodes
WHERE tv_show_id = :tv_show_id
GROUP BY season_number
ORDER BY season_number ASC
");
$stmt->execute(['tv_show_id' => $this->id]);
$seasonStats = $stmt->fetchAll(\PDO::FETCH_ASSOC);
$seasons = [];
// For each season, get the episodes and create a season object
foreach ($seasonStats as $stat) {
$seasonNumber = $stat['season_number'];
// Get episodes for this season
$stmt = $this->pdo->prepare("
SELECT e.*
FROM tv_episodes e
WHERE e.tv_show_id = :tv_show_id AND e.season_number = :season_number
ORDER BY e.episode_number ASC
");
$stmt->execute([
'tv_show_id' => $this->id,
'season_number' => $seasonNumber
]);
$episodes = $stmt->fetchAll(\PDO::FETCH_ASSOC);
// Create a season object (simulating the old seasons table structure)
$seasons[] = [
'id' => null, // No seasons table, so no ID
'season_number' => $seasonNumber,
'episode_count' => (int)$stat['episode_count'],
'watched_episodes' => (int)$stat['watched_episodes'],
'episodes' => $episodes
];
}
return $seasons;
}
/**
* Get similar TV shows based on genres
*/
public function getSimilarShows(int $limit = 6): array
{
$genres = $this->genre ? explode(',', $this->genre) : [];
$placeholders = str_repeat('?,', count($genres) - 1) . '?';
$sql = "
SELECT t.*,
COUNT(DISTINCT g.genre) as matching_genres
FROM tv_shows t
CROSS JOIN (SELECT TRIM(value) as genre
FROM json_each('[\"" . str_replace(',', '","', str_replace('"', '\\"', $this->genre)) . "\"]')
WHERE value != '') g
WHERE t.id != ?
AND t.genre LIKE '%' || g.genre || '%'
GROUP BY t.id
HAVING matching_genres > 0
ORDER BY matching_genres DESC, t.rating DESC
LIMIT ?
";
$params = array_merge([$this->id, $limit]);
$stmt = $this->pdo->prepare($sql);
$stmt->execute($params);
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
}
/**
* Record that this TV show was viewed
*/
public function recordView(): bool
{
$stmt = $this->pdo->prepare("
INSERT OR REPLACE INTO tv_show_views
(tv_show_id, view_count, last_viewed_at)
VALUES (?, COALESCE((SELECT view_count FROM tv_show_views WHERE tv_show_id = ?), 0) + 1, CURRENT_TIMESTAMP)
");
return $stmt->execute([$this->id, $this->id]);
}
/**
* Get all available genres from TV shows
*/
public static function getAvailableGenres(\PDO $pdo): array
{
$stmt = $pdo->query("
SELECT DISTINCT genre
FROM tv_shows
WHERE genre IS NOT NULL AND genre != ''
ORDER BY genre
");
return $stmt->fetchAll(\PDO::FETCH_COLUMN);
}
/**
* Get all available years from TV shows' first_air_date
*/
public static function getAvailableYears(\PDO $pdo): array
{
$stmt = $pdo->query("
SELECT DISTINCT YEAR(first_air_date) as year
FROM tv_shows
WHERE first_air_date IS NOT NULL
ORDER BY year DESC
");
return $stmt->fetchAll(\PDO::FETCH_COLUMN);
}
}