'int', 'rating' => 'float', 'watched' => 'bool', 'watch_count' => 'int', 'is_favorite' => 'bool', 'release_date' => 'date', 'last_watched_at' => 'datetime' ]; public function 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; } /** * Get total count of movies with optional filters */ public static function getTotalCount(\PDO $pdo, string $search = '', array $genres = [], array $directors = []): int { $where = []; $params = []; $sql = "SELECT COUNT(*) as count FROM movies m JOIN sources s ON m.source_id = s.id"; if (!empty($search)) { $where[] = "(m.title LIKE :search OR m.overview LIKE :search)"; $params[':search'] = "%$search%"; } if (!empty($genres)) { $genreConditions = []; foreach ($genres as $i => $genre) { $param = ":genre_$i"; $genreConditions[] = "m.genre LIKE $param"; $params[$param] = "%$genre%"; } $where[] = "(" . implode(' OR ', $genreConditions) . ")"; } if (!empty($directors)) { $directorConditions = []; foreach ($directors as $i => $director) { $param = ":director_$i"; $directorConditions[] = "m.director LIKE $param"; $params[$param] = "%$director%"; } $where[] = "(" . implode(' OR ', $directorConditions) . ")"; } if (!empty($where)) { $sql .= " WHERE " . implode(' AND ', $where); } $stmt = $pdo->prepare($sql); $stmt->execute($params); return (int)$stmt->fetchColumn(); } /** * Get paginated movies with optional filters */ public function getPaginated( \PDO $pdo, int $page = 1, int $perPage = 20, string $search = '', array $genres = [], string $sort = 'title_asc' ): array { $offset = ($page - 1) * $perPage; $where = []; $params = []; if (!empty($search)) { $where[] = "(title LIKE :search OR overview LIKE :search OR director LIKE :search OR writer LIKE :search)"; $params[':search'] = "%$search%"; } if (!empty($genres)) { $genreConditions = []; foreach ($genres as $i => $genre) { $param = ":genre$i"; $genreConditions[] = "genre LIKE $param"; $params[$param] = "%$genre%"; } $where[] = "(" . implode(' OR ', $genreConditions) . ")"; } // Determine sort order $orderBy = 'title ASC'; switch ($sort) { case 'title_desc': $orderBy = 'title DESC'; break; case 'release_asc': $orderBy = 'release_date ASC'; break; case 'release_desc': $orderBy = 'release_date DESC'; break; case 'rating_desc': $orderBy = 'rating DESC'; break; case 'rating_asc': $orderBy = 'rating ASC'; break; } $sql = "SELECT * FROM {$this->table}"; if (!empty($where)) { $sql .= " WHERE " . implode(' AND ', $where); } $sql .= " ORDER BY $orderBy LIMIT :limit OFFSET :offset"; $stmt = $pdo->prepare($sql); // Bind parameters foreach ($params as $key => $value) { $stmt->bindValue($key, $value); } $stmt->bindValue(':limit', $perPage, \PDO::PARAM_INT); $stmt->bindValue(':offset', $offset, \PDO::PARAM_INT); $stmt->execute(); return $stmt->fetchAll(\PDO::FETCH_ASSOC); } /** * Get all unique genres from movies */ public function getGenres(\PDO $pdo): array { $stmt = $pdo->query("SELECT DISTINCT genre FROM {$this->table} WHERE genre IS NOT NULL AND genre != ''"); $results = $stmt->fetchAll(\PDO::FETCH_COLUMN); $genres = []; foreach ($results as $genreList) { $genreArray = array_map('trim', explode(',', $genreList)); $genres = array_merge($genres, $genreArray); } $genres = array_unique($genres); sort($genres); return array_values(array_filter($genres)); } /** * Get all unique directors from movies */ public function getDirectors(\PDO $pdo): array { $stmt = $pdo->query("SELECT DISTINCT director FROM {$this->table} WHERE director IS NOT NULL AND director != '' ORDER BY director"); return $stmt->fetchAll(\PDO::FETCH_COLUMN); } public function markAsWatched(): bool { $this->watched = true; $this->watch_count += 1; $this->last_watched_at = date('Y-m-d H:i:s'); return $this->update($this->id, [ 'watched' => $this->watched, 'watch_count' => $this->watch_count, 'last_watched_at' => $this->last_watched_at ]); } public function markAsUnwatched(): bool { $this->watched = false; return $this->update($this->id, [ 'watched' => $this->watched ]); } public function toggleFavorite(): bool { return $this->update($this->id, [ 'is_favorite' => !$this->is_favorite ]); } public function updateRating(float $rating): bool { return $this->update($this->id, [ 'rating' => min(10.0, max(0.0, $rating)) ]); } public static function getStats(\PDO $pdo): array { $stmt = $pdo->query(" SELECT COUNT(*) as total_movies, COUNT(CASE WHEN watched = 1 THEN 1 END) as watched_movies, SUM(watch_count) as total_watches, AVG(rating) as avg_rating, COUNT(CASE WHEN is_favorite = 1 THEN 1 END) as favorite_movies, SUM(runtime_minutes) as total_runtime FROM movies "); return $stmt->fetch(\PDO::FETCH_ASSOC); } public static function getRecent(\PDO $pdo, int $limit = 10): array { $stmt = $pdo->prepare(" SELECT m.*, s.display_name as source_name FROM movies m JOIN sources s ON m.source_id = s.id WHERE m.last_watched_at IS NOT NULL ORDER BY m.last_watched_at DESC LIMIT :limit "); $stmt->execute(['limit' => $limit]); return $stmt->fetchAll(\PDO::FETCH_ASSOC); } /* public static function getTotalCount(\PDO $pdo, string $search = '', array $genres = [], array $directors = []): int { $sql = "SELECT COUNT(*) as count FROM movies m JOIN sources s ON m.source_id = s.id"; $params = []; if (!empty($search)) { $sql .= " WHERE (m.title LIKE :search OR m.overview 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 . " m.genre IN (" . implode(',', $placeholders) . ")"; } if (!empty($directors)) { $placeholders = []; foreach ($directors as $index => $director) { $placeholders[] = ":director_{$index}"; $params["director_{$index}"] = $director; } $whereClause = (!empty($search) || !empty($genres)) ? " AND" : " WHERE"; $sql .= $whereClause . " m.director IN (" . implode(',', $placeholders) . ")"; } $stmt = $pdo->prepare($sql); foreach ($params as $key => $value) { $stmt->bindValue(":{$key}", $value); } $stmt->execute(); return (int) $stmt->fetch()['count']; } */ public static function getAllWithPagination(\PDO $pdo, int $page, int $perPage, string $search = '', array $genres = [], array $directors = [], string $sort = 'title_asc'): array { $offset = ($page - 1) * $perPage; $sql = " SELECT m.*, s.display_name as source_name FROM movies m JOIN sources s ON m.source_id = s.id "; $params = []; if (!empty($search)) { $sql .= " WHERE (m.title LIKE :search OR m.overview 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 . " ("; foreach ($placeholders as $i => $placeholder) { if ($i > 0) $sql .= " OR "; $sql .= "m.genre LIKE $placeholder"; } $sql .= ")"; } if (!empty($directors)) { $placeholders = []; foreach ($directors as $index => $director) { $placeholders[] = ":director_{$index}"; $params["director_{$index}"] = "%$director%"; } $whereClause = (!empty($search) || !empty($genres)) ? " AND" : " WHERE"; $sql .= $whereClause . " ("; foreach ($placeholders as $i => $placeholder) { if ($i > 0) $sql .= " OR "; $sql .= "m.director LIKE $placeholder"; } $sql .= ")"; } // Add sorting $sortOptions = [ 'title_asc' => 'm.title ASC', 'title_desc' => 'm.title DESC', 'year_asc' => 'm.release_date ASC', 'year_desc' => 'm.release_date DESC', 'rating_asc' => 'm.rating ASC NULLS LAST', 'rating_desc' => 'm.rating DESC NULLS LAST', 'views_asc' => 'm.watch_count ASC', 'views_desc' => 'm.watch_count DESC', 'added_asc' => 'm.created_at ASC', 'added_desc' => 'm.created_at DESC', 'last_watched_asc' => 'm.last_watched_at ASC NULLS LAST', 'last_watched_desc' => 'm.last_watched_at DESC NULLS LAST' ]; $sortClause = $sortOptions[$sort] ?? 'm.title ASC'; $sql .= " ORDER BY $sortClause 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); } public static function getAll(\PDO $pdo, int $limit = 100): array { $stmt = $pdo->prepare(" SELECT m.*, s.display_name as source_name FROM movies m JOIN sources s ON m.source_id = s.id ORDER BY m.title ASC LIMIT :limit "); $stmt->execute(['limit' => $limit]); return $stmt->fetchAll(\PDO::FETCH_ASSOC); } /** * Get all actors associated with this movie */ public function actors() { $stmt = $this->pdo->prepare(" SELECT a.* FROM actors a JOIN actor_movie am ON a.id = am.actor_id WHERE am.movie_id = :movie_id ORDER BY a.name ASC "); $stmt->execute(['movie_id' => $this->id]); return $stmt->fetchAll(\PDO::FETCH_ASSOC); } /** * Add an actor to this movie */ public function addActor(int $actorId): bool { $stmt = $this->pdo->prepare(" INSERT IGNORE INTO actor_movie (movie_id, actor_id) VALUES (:movie_id, :actor_id) "); return $stmt->execute([ 'movie_id' => $this->id, 'actor_id' => $actorId ]); } /** * Remove an actor from this movie */ public function removeActor(int $actorId): bool { $stmt = $this->pdo->prepare(" DELETE FROM actor_movie WHERE movie_id = :movie_id AND actor_id = :actor_id "); return $stmt->execute([ 'movie_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 available genres for filtering */ public static function getAvailableGenres(\PDO $pdo): array { $stmt = $pdo->query(" SELECT DISTINCT genre FROM movies WHERE genre IS NOT NULL AND genre != '' ORDER BY genre "); return $stmt->fetchAll(\PDO::FETCH_COLUMN); } /** * Get available directors for filtering */ public static function getAvailableDirectors(\PDO $pdo): array { $stmt = $pdo->query(" SELECT DISTINCT director FROM movies WHERE director IS NOT NULL AND director != '' ORDER BY director "); return $stmt->fetchAll(\PDO::FETCH_COLUMN); } }