Apply strict_types and extensive type declarations throughout the API and models, improving type safety and error handling. Key changes: add declare(strict_types=1) to many files; convert properties, method parameters and return values to typed signatures (PDO, arrays, ints, strings, bools, nullables); switch exception handling to Throwable in index and Router; improve Router, controllers and model method signatures and nullability handling; refine file/image serving security checks and headers in ImageController; strengthen Database typing and initialization methods; return explicit types from BaseModel CRUD helpers and counting; update Media/Cast/Adult/Game/Console/Settings controllers and models to use typed methods, better validation, and clearer update/create return types. Also add AGENTS.md (agent skills index), update README with Swagger/OpenAPI usage instructions, and add /.windsurf to .gitignore. These changes aim to harden runtime correctness, make intended contracts explicit, and prepare the codebase for easier maintenance and static analysis.
143 lines
4.5 KiB
PHP
143 lines
4.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/MediaType.php';
|
|
|
|
class Series extends MediaType {
|
|
protected function getType(): string {
|
|
return 'TV';
|
|
}
|
|
|
|
protected function getTypeSpecificFields(): array {
|
|
return ['runtime', 'director', 'writer'];
|
|
}
|
|
|
|
protected function validateTypeSpecificFields(array $data): array {
|
|
// Series Validierung
|
|
if (isset($data['runtime']) && !is_numeric($data['runtime'])) {
|
|
throw new Exception('Runtime must be a number');
|
|
}
|
|
return $data;
|
|
}
|
|
|
|
public function search(array $filters = [], int $page = 1, int $limit = 20): array {
|
|
// Nur Series suchen
|
|
$filters['type'] = 'TV';
|
|
return parent::search($filters, $page, $limit);
|
|
}
|
|
|
|
public function getWithEpisodes(?int $id): ?array {
|
|
$media = $this->getWithRelations($id);
|
|
if (!$media) {
|
|
return null;
|
|
}
|
|
|
|
$media['episodes'] = $this->getEpisodes($id);
|
|
$media['seasons'] = $this->getSeasons($id);
|
|
|
|
return $media;
|
|
}
|
|
|
|
public function getEpisodes(?int $mediaId, ?int $season = null): array {
|
|
$query = "SELECT * FROM episodes WHERE media_id = ?";
|
|
$params = [$mediaId];
|
|
|
|
if ($season !== null) {
|
|
$query .= " AND season = ?";
|
|
$params[] = $season;
|
|
}
|
|
|
|
$query .= " ORDER BY season, episode_number";
|
|
|
|
$stmt = $this->pdo->prepare($query);
|
|
$stmt->execute($params);
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
public function getSeasons(?int $mediaId): array {
|
|
$stmt = $this->pdo->prepare("
|
|
SELECT DISTINCT season, COUNT(*) as episode_count,
|
|
MIN(air_date) as first_air_date, MAX(air_date) as last_air_date
|
|
FROM episodes
|
|
WHERE media_id = ?
|
|
GROUP BY season
|
|
ORDER BY season
|
|
");
|
|
$stmt->execute([$mediaId]);
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
public function addEpisode(?int $mediaId, array $episodeData): int {
|
|
$stmt = $this->pdo->prepare("
|
|
INSERT INTO episodes (media_id, season, episode_number, title, description, air_date, duration, thumbnail)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
");
|
|
$stmt->execute([
|
|
$mediaId,
|
|
$episodeData['season'] ?? 1,
|
|
$episodeData['episode_number'] ?? null,
|
|
$episodeData['title'] ?? null,
|
|
$episodeData['description'] ?? null,
|
|
$episodeData['air_date'] ?? null,
|
|
$episodeData['duration'] ?? null,
|
|
$episodeData['thumbnail'] ?? null
|
|
]);
|
|
return (int)$this->pdo->lastInsertId();
|
|
}
|
|
|
|
public function updateEpisode(?int $episodeId, array $episodeData): bool {
|
|
$fields = [];
|
|
$params = [];
|
|
|
|
foreach (['season', 'episode_number', 'title', 'description', 'air_date', 'duration', 'thumbnail'] as $field) {
|
|
if (array_key_exists($field, $episodeData)) {
|
|
$fields[] = "$field = ?";
|
|
$params[] = $episodeData[$field];
|
|
}
|
|
}
|
|
|
|
if (!empty($fields)) {
|
|
$params[] = $episodeId;
|
|
$stmt = $this->pdo->prepare("UPDATE episodes SET " . implode(', ', $fields) . " WHERE id = ?");
|
|
$stmt->execute($params);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function deleteEpisode(?int $episodeId): bool {
|
|
$stmt = $this->pdo->prepare("DELETE FROM episodes WHERE id = ?");
|
|
$stmt->execute([$episodeId]);
|
|
return $stmt->rowCount() > 0;
|
|
}
|
|
|
|
public function createWithRelations(array $data): int {
|
|
$mediaId = parent::createWithRelations($data);
|
|
|
|
// Episoden speichern
|
|
if (isset($data['episodes']) && is_array($data['episodes'])) {
|
|
foreach ($data['episodes'] as $episode) {
|
|
$this->addEpisode($mediaId, $episode);
|
|
}
|
|
}
|
|
|
|
return $mediaId;
|
|
}
|
|
|
|
public function updateWithRelations(int $id, array $data): bool {
|
|
parent::updateWithRelations($id, $data);
|
|
|
|
// Episoden aktualisieren
|
|
if (isset($data['episodes']) && is_array($data['episodes'])) {
|
|
// Alle existierenden Episoden löschen
|
|
$this->pdo->prepare("DELETE FROM episodes WHERE media_id = ?")->execute([$id]);
|
|
// Neue Episoden hinzufügen
|
|
foreach ($data['episodes'] as $episode) {
|
|
$this->addEpisode($id, $episode);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|