Files
MediaCollectorLibary/app/Models/Movie.php
Lars Behrends 04140786a7 Stuff i guess ?
2025-10-31 00:24:17 +01:00

472 lines
14 KiB
PHP

<?php
namespace App\Models;
class Movie extends Model
{
protected string $table = 'movies';
protected array $fillable = [
'title',
'overview',
'director',
'writer',
'genre',
'cast',
'release_date',
'runtime_minutes',
'rating',
'imdb_id',
'tmdb_id',
'poster_url',
'backdrop_url',
'watched',
'watch_count',
'is_favorite',
'metadata',
'source_id',
'last_watched_at'
];
protected array $casts = [
'runtime_minutes' => '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);
}
}