Initial project scaffold for a PHP Media API including routing, controllers, models and services under api/ (Router, Media/Cast/Image/Settings controllers, models, database/bootstrap files and automatic docs service). Adds Docker support (Dockerfile, docker-compose.yml, DOCKER_README.md, php-custom.ini), .htaccess for pretty URLs, API documentation and example payloads (API_EXAMPLES.md, api/README.md, api_examples/*.json), image handling service and logging, plus a comprehensive .gitignore. This commit provides a runnable development environment and example requests to get the API up and tested quickly.
191 lines
6.7 KiB
PHP
191 lines
6.7 KiB
PHP
<?php
|
|
|
|
require_once __DIR__ . '/BaseModel.php';
|
|
require_once __DIR__ . '/../services/ImageHandler.php';
|
|
|
|
class Cast extends BaseModel {
|
|
protected $table = 'cast_staff';
|
|
private $imageHandler;
|
|
protected $isUpdate = false;
|
|
protected $castId = null;
|
|
|
|
public function __construct($pdo) {
|
|
parent::__construct($pdo);
|
|
$this->imageHandler = new ImageHandler();
|
|
}
|
|
|
|
public function getWithFilmography($id) {
|
|
$cast = $this->findById($id);
|
|
if (!$cast) {
|
|
return null;
|
|
}
|
|
|
|
$cast['occupations'] = $this->getRelatedItems('occupations', $id, 'cast_id');
|
|
$cast['filmography'] = $this->getMediaForCast($id);
|
|
|
|
return $cast;
|
|
}
|
|
|
|
public function getMediaForCast($castId) {
|
|
$stmt = $this->pdo->prepare("
|
|
SELECT m.id, m.title, m.year, m.poster, m.category, m.type, mc.role, mc.characterName
|
|
FROM media m
|
|
INNER JOIN media_cast mc ON m.id = mc.media_id
|
|
WHERE mc.cast_id = ?
|
|
ORDER BY m.year DESC
|
|
");
|
|
$stmt->execute([$castId]);
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
public function search($filters = [], $page = 1, $limit = 20) {
|
|
$conditions = [];
|
|
|
|
if (isset($filters['search'])) {
|
|
$conditions['name'] = ["%" . $filters['search'] . "%"];
|
|
}
|
|
|
|
$offset = ($page - 1) * $limit;
|
|
$items = $this->findAll($conditions, 'createdAt DESC', $limit, $offset);
|
|
$total = $this->count($conditions);
|
|
|
|
|
|
// Add filmography to each cast member
|
|
foreach ($items as &$item) {
|
|
$item['filmography'] = $this->getMediaForCast($item['id']);
|
|
// Extract unique media types
|
|
$mediaTypes = array_unique(array_column($item['filmography'], 'category'));
|
|
$item['media_types'] = array_values($mediaTypes);
|
|
}
|
|
|
|
|
|
return [
|
|
'items' => $items,
|
|
'total' => $total,
|
|
'page' => $page,
|
|
'limit' => $limit
|
|
];
|
|
}
|
|
|
|
protected function processPhotoField($data) {
|
|
error_log("Cast::processPhotoField - Checking for photo field, isUpdate: " . ($this->isUpdate ? 'yes' : 'no'));
|
|
|
|
if ($this->isUpdate && $this->castId && isset($data['photo']) && !empty($data['photo'])) {
|
|
$currentCast = $this->findById($this->castId);
|
|
if ($currentCast && !empty($currentCast['photo'])) {
|
|
$oldPhoto = $currentCast['photo'];
|
|
if (strpos($oldPhoto, '/images/') === 0) {
|
|
error_log("Cast::processPhotoField - Deleting old photo: " . $oldPhoto);
|
|
$this->imageHandler->deleteImage($oldPhoto);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isset($data['photo']) && !empty($data['photo'])) {
|
|
error_log("Cast::processPhotoField - Photo found, length: " . strlen($data['photo']));
|
|
|
|
if (strpos($data['photo'], '/images/') === 0 || filter_var($data['photo'], FILTER_VALIDATE_URL)) {
|
|
error_log("Cast::processPhotoField - Photo is already a path or URL, skipping processing");
|
|
return $data;
|
|
}
|
|
|
|
$photoPath = $this->imageHandler->saveBase64Image($data['photo'], 'cast/photo');
|
|
error_log("Cast::processPhotoField - ImageHandler returned: " . ($photoPath ?: 'null'));
|
|
if ($photoPath) {
|
|
$data['photo'] = $photoPath;
|
|
error_log("Cast::processPhotoField - Photo path set to: " . $photoPath);
|
|
} else {
|
|
error_log("Cast::processPhotoField - Failed to process photo, keeping original data");
|
|
}
|
|
} else {
|
|
error_log("Cast::processPhotoField - No photo field found or empty");
|
|
}
|
|
return $data;
|
|
}
|
|
|
|
public function createWithOccupations($data) {
|
|
$name = $data['name'] ?? null;
|
|
if (!$name) {
|
|
throw new Exception('Name is required');
|
|
}
|
|
|
|
// cleanname generieren
|
|
$cleanname = generateCleanName($name);
|
|
|
|
$data = $this->processPhotoField($data);
|
|
|
|
$castData = [
|
|
'name' => $name,
|
|
'cleanname' => $cleanname,
|
|
'photo' => $data['photo'] ?? null,
|
|
'bio' => $data['bio'] ?? null,
|
|
'birthDate' => $data['birthDate'] ?? null,
|
|
'birthPlace' => $data['birthPlace'] ?? null
|
|
];
|
|
|
|
$castId = $this->create($castData);
|
|
|
|
if (isset($data['occupations']) && is_array($data['occupations'])) {
|
|
$this->saveRelatedItems('occupations', $castId, $data['occupations'], 'cast_id');
|
|
}
|
|
|
|
return $castId;
|
|
}
|
|
|
|
public function updateWithOccupations($id, $data) {
|
|
$this->isUpdate = true;
|
|
$this->castId = $id;
|
|
|
|
$data = $this->processPhotoField($data);
|
|
|
|
$castData = [];
|
|
|
|
foreach (['name', 'photo', 'bio', 'birthDate', 'birthPlace'] as $field) {
|
|
if (array_key_exists($field, $data)) {
|
|
$castData[$field] = $data[$field];
|
|
}
|
|
}
|
|
|
|
// Wenn name geändert wurde, cleanname aktualisieren
|
|
if (isset($data['name'])) {
|
|
$castData['cleanname'] = generateCleanName($data['name']);
|
|
}
|
|
|
|
if (!empty($castData)) {
|
|
$this->update($id, $castData);
|
|
}
|
|
|
|
if (isset($data['occupations']) && is_array($data['occupations'])) {
|
|
$this->pdo->prepare("DELETE FROM occupations WHERE cast_id = ?")->execute([$id]);
|
|
$this->saveRelatedItems('occupations', $id, $data['occupations'], 'cast_id');
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public function findByCleanName($cleanname) {
|
|
$stmt = $this->pdo->prepare("SELECT * FROM {$this->table} WHERE cleanname = ?");
|
|
$stmt->execute([$cleanname]);
|
|
return $stmt->fetch();
|
|
}
|
|
|
|
protected function getRelatedItems($table, $id, $fkColumn = 'media_id') {
|
|
$stmt = $this->pdo->prepare("SELECT * FROM $table WHERE $fkColumn = ?");
|
|
$stmt->execute([$id]);
|
|
$items = $stmt->fetchAll();
|
|
$result = [];
|
|
foreach ($items as $item) {
|
|
$result[] = $fkColumn === 'cast_id' ? $item['occupation'] : $item['genre'];
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
protected function saveRelatedItems($table, $id, $items, $fkColumn = 'media_id') {
|
|
$valueColumn = $fkColumn === 'cast_id' ? 'occupation' : 'genre';
|
|
$stmt = $this->pdo->prepare("INSERT INTO $table ($fkColumn, $valueColumn) VALUES (?, ?)");
|
|
foreach ($items as $item) {
|
|
$stmt->execute([$id, $item]);
|
|
}
|
|
}
|
|
}
|