'int', 'episode_number' => 'int', 'runtime_minutes' => 'int', 'rating' => 'float', 'is_watched' => 'bool', 'is_favorite' => 'bool', 'air_date' => 'date', 'metadata' => 'array' ]; /** * Get all actors associated with this TV episode */ public function actors() { $stmt = $this->pdo->prepare(" SELECT a.* FROM actors a JOIN actor_tv_episode ate ON a.id = ate.actor_id WHERE ate.tv_episode_id = :tv_episode_id ORDER BY a.name ASC "); $stmt->execute(['tv_episode_id' => $this->id]); return $stmt->fetchAll(\PDO::FETCH_ASSOC); } /** * Get the TV show this episode belongs to */ public function tvShow() { $stmt = $this->pdo->prepare(" SELECT * FROM tv_shows WHERE id = :tv_show_id "); $stmt->execute(['tv_show_id' => $this->tv_show_id]); $showData = $stmt->fetch(\PDO::FETCH_ASSOC); return $showData ? new TvShow($this->pdo, $showData) : null; } /** * Get TV episode statistics */ public static function getStats(\PDO $pdo): array { $stmt = $pdo->query(" SELECT COUNT(*) as total_episodes, COUNT(CASE WHEN is_watched = 1 THEN 1 END) as watched_episodes, COUNT(CASE WHEN is_favorite = 1 THEN 1 END) as favorite_episodes, AVG(rating) as avg_rating FROM tv_episodes "); return $stmt->fetch(\PDO::FETCH_ASSOC); } /** * Get total count with optional search */ public static function getTotalCount(\PDO $pdo, string $search = ''): int { $sql = "SELECT COUNT(*) as count FROM tv_episodes te JOIN tv_shows ts ON te.tv_show_id = ts.id JOIN sources s ON te.source_id = s.id"; $params = []; if (!empty($search)) { $sql .= " WHERE te.title LIKE :search OR ts.title LIKE :search"; $params['search'] = "%{$search}%"; } $stmt = $pdo->prepare($sql); $stmt->execute($params); return (int) $stmt->fetch()['count']; } /** * Get all TV episodes with pagination and optional search */ public static function getAllWithPagination(\PDO $pdo, int $page, int $perPage, string $search = ''): array { $offset = ($page - 1) * $perPage; $sql = " SELECT te.*, ts.title as show_title, s.display_name as source_name FROM tv_episodes te JOIN tv_shows ts ON te.tv_show_id = ts.id JOIN sources s ON te.source_id = s.id "; $params = []; if (!empty($search)) { $sql .= " WHERE te.title LIKE :search OR ts.title LIKE :search"; $params['search'] = "%{$search}%"; } $sql .= " ORDER BY ts.title ASC, te.season_number ASC, te.episode_number 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 watched status */ public function toggleWatched(): bool { return $this->update($this->id, [ 'is_watched' => !$this->is_watched ]); } /** * 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; } }