纯结构性重构,不改变任何功能。 - config.php 由 739 行缩减为 42 行,改为纯引导入口;新增 ROOT_DIR 常量 统一拼接绝对路径(lib/ 内模块的 __DIR__ 会指向 lib/,必须替换) - 新增 lib/ 五个模块 db.php / helpers.php / auth.php / movies.php / stats.php, 由 config.php 按 db → helpers → auth → movies → stats 顺序加载 - add.php、edit.php 各自约 80 行重复的 POST 处理改为调用 resolveCinemaId / resolvePersonIds / linkViewers / uploadImageFile / saveBase64Image / uploadMultiplePhotos / deleteStoredFile - cinemas.php、persons.php 中重复 4 次的颜色校验改用 normalizeHexColor() - toggleNewViewer / removeViewerRow / toggleNewCinema 下沉至 js/main.js, add.php 中的 removeViewer 统一更名为 removeViewerRow - 删除死代码:imageUrl()(全项目无调用);cinemas.php 配色数组中本就会被 array_slice 丢弃的 extra 颜色逻辑,简化为单条扁平 50 元素数组 - README 目录结构补充 lib/ 说明 验证方式:以便携 PHP 8.4 搭建 A/B 回归环境,播种固定数据后遍历 47 个 GET/POST 端点录制「状态码 + 响应体」。重构前后 42 个逐字节相同、2 个仅 上传文件名时间戳不同、3 个差异均为内联脚本下沉(已单独校验函数体逐字符 等价,剥离脚本后 HTML 标记完全一致)。24 个 PHP 文件语法检查全部通过。
197 lines
6.5 KiB
PHP
197 lines
6.5 KiB
PHP
<?php
|
|
// ============================================
|
|
// 文件: lib/movies.php
|
|
// 说明: 电影 / 影院读取与搜索,以及表单提交时的关联数据处理
|
|
// ============================================
|
|
|
|
// ===== 电影列表 =====
|
|
function getMovies() {
|
|
$db = getDB();
|
|
$stmt = $db->query("
|
|
SELECT m.*, c.place, c.name as cinema_name, c.address as cinema_address, c.color as cinema_color
|
|
FROM movies m
|
|
LEFT JOIN cinemas c ON m.cinema_id = c.id
|
|
ORDER BY m.watch_date DESC, m.created_at DESC
|
|
");
|
|
$movies = $stmt->fetchAll();
|
|
attachViewersToMovies($movies);
|
|
return $movies;
|
|
}
|
|
|
|
function getMovie($id) {
|
|
$db = getDB();
|
|
$stmt = $db->prepare("
|
|
SELECT m.*, c.place, c.name as cinema_name, c.address as cinema_address, c.color as cinema_color
|
|
FROM movies m
|
|
LEFT JOIN cinemas c ON m.cinema_id = c.id
|
|
WHERE m.id = ?
|
|
");
|
|
$stmt->execute([$id]);
|
|
$movie = $stmt->fetch();
|
|
if ($movie) {
|
|
$stmt2 = $db->prepare("
|
|
SELECT v.id, v.movie_id, v.person_id,
|
|
COALESCE(p.name, v.name) as name,
|
|
p.color as person_color,
|
|
p.avatar as person_avatar
|
|
FROM viewers v
|
|
LEFT JOIN persons p ON v.person_id = p.id
|
|
WHERE v.movie_id = ?
|
|
ORDER BY v.id
|
|
");
|
|
$stmt2->execute([$id]);
|
|
$movie['viewers'] = $stmt2->fetchAll();
|
|
|
|
$stmt3 = $db->prepare("SELECT * FROM movie_photos WHERE movie_id = ? ORDER BY id");
|
|
$stmt3->execute([$id]);
|
|
$movie['photos'] = $stmt3->fetchAll();
|
|
}
|
|
return $movie;
|
|
}
|
|
|
|
function attachViewersToMovies(&$movies) {
|
|
if (empty($movies)) return;
|
|
$db = getDB();
|
|
$movie_ids = array_column($movies, 'id');
|
|
if (empty($movie_ids)) return;
|
|
$placeholders = implode(',', array_fill(0, count($movie_ids), '?'));
|
|
$stmt = $db->prepare("
|
|
SELECT v.movie_id, p.id as person_id, p.name, p.color, p.avatar
|
|
FROM viewers v
|
|
LEFT JOIN persons p ON v.person_id = p.id
|
|
WHERE v.movie_id IN ($placeholders)
|
|
ORDER BY v.id
|
|
");
|
|
$stmt->execute($movie_ids);
|
|
$rows = $stmt->fetchAll();
|
|
$viewers_map = [];
|
|
foreach ($rows as $row) {
|
|
$movie_id = $row['movie_id'];
|
|
if (!isset($viewers_map[$movie_id])) $viewers_map[$movie_id] = [];
|
|
$viewers_map[$movie_id][] = [
|
|
'person_id' => $row['person_id'],
|
|
'name' => $row['name'] ?? '未知',
|
|
'color' => $row['color'] ?? '#888888',
|
|
'avatar' => $row['avatar']
|
|
];
|
|
}
|
|
foreach ($movies as &$movie) {
|
|
$movie['viewers'] = $viewers_map[$movie['id']] ?? [];
|
|
}
|
|
}
|
|
|
|
// ===== 影院 =====
|
|
function getCinemas() {
|
|
$db = getDB();
|
|
$stmt = $db->query("SELECT * FROM cinemas ORDER BY place, name");
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
// ===== 搜索 =====
|
|
function searchMovies($keyword) {
|
|
return advancedSearch(['title' => $keyword]);
|
|
}
|
|
|
|
function advancedSearch($params) {
|
|
$db = getDB();
|
|
$sql = "
|
|
SELECT DISTINCT m.*,
|
|
c.place, c.name as cinema_name, c.address as cinema_address, c.color as cinema_color
|
|
FROM movies m
|
|
LEFT JOIN cinemas c ON m.cinema_id = c.id
|
|
LEFT JOIN viewers v ON m.id = v.movie_id
|
|
LEFT JOIN persons p ON v.person_id = p.id
|
|
WHERE 1=1
|
|
";
|
|
$bind = [];
|
|
if (!empty($params['title'])) {
|
|
$sql .= " AND m.title LIKE :title";
|
|
$bind[':title'] = '%' . $params['title'] . '%';
|
|
}
|
|
if (!empty($params['cinema_id']) && is_numeric($params['cinema_id'])) {
|
|
$sql .= " AND m.cinema_id = :cinema_id";
|
|
$bind[':cinema_id'] = (int)$params['cinema_id'];
|
|
}
|
|
if (!empty($params['person_id']) && is_numeric($params['person_id'])) {
|
|
$sql .= " AND v.person_id = :person_id";
|
|
$bind[':person_id'] = (int)$params['person_id'];
|
|
}
|
|
if (!empty($params['date_from'])) {
|
|
$sql .= " AND m.watch_date >= :date_from";
|
|
$bind[':date_from'] = $params['date_from'];
|
|
}
|
|
if (!empty($params['date_to'])) {
|
|
$sql .= " AND m.watch_date <= :date_to";
|
|
$bind[':date_to'] = $params['date_to'];
|
|
}
|
|
$sql .= " ORDER BY m.watch_date DESC, m.created_at DESC";
|
|
$stmt = $db->prepare($sql);
|
|
$stmt->execute($bind);
|
|
$movies = $stmt->fetchAll();
|
|
attachViewersToMovies($movies);
|
|
return $movies;
|
|
}
|
|
|
|
// ===== 表单提交时的关联数据处理 =====
|
|
|
|
/**
|
|
* 解析表单提交的电影院选择。
|
|
* 选择「新建」且填了名称时创建影院并返回新 id;未选择或信息不足时返回 null。
|
|
*/
|
|
function resolveCinemaId($cinema_id, $new_place, $new_name, $new_address) {
|
|
$db = getDB();
|
|
|
|
if ($cinema_id === 'new' && !empty($new_name)) {
|
|
$stmt = $db->prepare("INSERT INTO cinemas (place, name, address) VALUES (?, ?, ?)");
|
|
$stmt->execute([$new_place, $new_name, $new_address]);
|
|
$cinema_id = $db->lastInsertId();
|
|
} elseif ($cinema_id === 'new') {
|
|
$cinema_id = null;
|
|
} elseif (!empty($cinema_id) && is_numeric($cinema_id)) {
|
|
$cinema_id = (int)$cinema_id;
|
|
} else {
|
|
$cinema_id = null;
|
|
}
|
|
|
|
return $cinema_id;
|
|
}
|
|
|
|
/**
|
|
* 解析「一起看的人」表单行:已有观影人取 id,选择「新增」则按姓名建库或复用。
|
|
* 返回去重后的观影人 id 列表。
|
|
*/
|
|
function resolvePersonIds($viewer_selects, $viewer_news) {
|
|
$db = getDB();
|
|
$person_ids = [];
|
|
foreach ($viewer_selects as $idx => $val) {
|
|
if ($val === 'new') {
|
|
$new_name = trim($viewer_news[$idx] ?? '');
|
|
if (!empty($new_name)) {
|
|
$chk = $db->prepare("SELECT id FROM persons WHERE name = ?");
|
|
$chk->execute([$new_name]);
|
|
$existing = $chk->fetch();
|
|
if ($existing) {
|
|
$person_ids[] = $existing['id'];
|
|
} else {
|
|
$ins = $db->prepare("INSERT INTO persons (name) VALUES (?)");
|
|
$ins->execute([$new_name]);
|
|
$person_ids[] = $db->lastInsertId();
|
|
}
|
|
}
|
|
} elseif (is_numeric($val) && $val > 0) {
|
|
$person_ids[] = (int)$val;
|
|
}
|
|
}
|
|
return array_unique($person_ids);
|
|
}
|
|
|
|
// 把观影人写入电影关联表
|
|
function linkViewers($movie_id, $person_ids) {
|
|
if (empty($person_ids)) return;
|
|
$db = getDB();
|
|
$stmt = $db->prepare("INSERT INTO viewers (movie_id, person_id, name) VALUES (?, ?, ?)");
|
|
foreach ($person_ids as $pid) {
|
|
$stmt->execute([$movie_id, $pid, '']);
|
|
}
|
|
}
|