PHP+MySQL房间预订系统如何比对下拉时间与库中数据禁用已约时段
PHP+MySQL房间预订系统时段冲突禁用实现方案
注意:所有时间比对需要基于用户选定的具体预订日期执行,跨天预订请单独拆分时段处理,避免判断错误
1. 后端查询指定日期已占用时段
首先通过预处理语句查询选定日期下所有状态有效的预订记录,遍历记录把所有被覆盖的整点时段存入占用数组,必须使用参数化查询,避免SQL注入风险。
示例代码(基于PDO):
// 接收前端传入的选定预订日期,做好格式校验 $book_date = $_POST['book_date']; // 初始化已占用时间数组 $occupied_times = []; // 数据库连接省略,$pdo为已实例化的PDO连接对象 // 如果start、end是DATETIME类型,把查询条件改为DATE(start) = ?即可 $stmt = $pdo->prepare("SELECT start, end FROM reservations WHERE book_date = ? AND status = 'confirmed'"); $stmt->execute([$book_date]); $reservations = $stmt->fetchAll(PDO::FETCH_ASSOC); // 遍历所有预订记录,标记被占用的时段 foreach ($reservations as $res) { $start_ts = strtotime($res['start']); $end_ts = strtotime($res['end']); // 从预订开始时间到结束时间前的所有整点时段均被占用,结束时间可作为下一个预订的起点 for ($t = $start_ts; $t < $end_ts; $t += 3600) { $occupied_times[] = date('H:i:s', $t); } }
2. 动态渲染下拉框,自动禁用冲突选项
不要硬写option标签,把可选时段定义为数组,渲染时判断当前时段是否在占用数组中,是则添加disabled属性禁止选择。
// 定义开始时间可选范围 $from_options = [ '08:00:00' => '8 a.m.', '09:00:00' => '9 a.m.', '10:00:00' => '10 a.m.', '11:00:00' => '11 a.m.', '12:00:00' => '12 p.m.', '13:00:00' => '1 p.m.', '14:00:00' => '2 p.m.', '15:00:00' => '3 p.m.', '16:00:00' => '4 p.m.', '17:00:00' => '5 p.m.', '18:00:00' => '6 p.m.', '19:00:00' => '7 p.m.', '20:00:00' => '8 p.m.', '21:00:00' => '9 p.m.' ]; // 定义结束时间可选范围 $to_options = [ '09:00:00' => '9 a.m.', '10:00:00' => '10 a.m.', '11:00:00' => '11 a.m.', '12:00:00' => '12 p.m.', '13:00:00' => '1 p.m.', '14:00:00' => '2 p.m.', '15:00:00' => '3 p.m.', '16:00:00' => '4 p.m.', '17:00:00' => '5 p.m.', '18:00:00' => '6 p.m.', '19:00:00' => '7 p.m.', '20:00:00' => '8 p.m.', '21:00:00' => '9 p.m.', '22:00:00' => '10 p.m.' ];
渲染开始时间下拉框:
<div class="col-md-6"> <label for="from" class="fromLabel">From</label> <select required name="from" id="from" class="form-select form-select-lg"> <option selected disabled value="">Pick a time</option> <?php foreach ($from_options as $time_val => $time_text): ?> <option value="<?= $time_val ?>" <?= in_array($time_val, $occupied_times) ? 'disabled' : '' ?>> <?= $time_text ?> </option> <?php endforeach; ?> </select> </div>
3. 前端联动校验结束时间
用户选择开始时间后,自动禁用早于开始时间、以及和已占用时段冲突的结束时间选项,提升交互体验:
const fromSelect = document.getElementById('from'); const toSelect = document.getElementById('to'); // 把后端计算的已占用时间传到前端 const occupiedTimes = <?= json_encode($occupied_times) ?>; fromSelect.addEventListener('change', function() { const selectedFrom = this.value; // 重置所有结束时间选项状态 Array.from(toSelect.options).forEach(opt => { if (!opt.value) return; opt.disabled = false; // 结束时间早于等于开始时间,直接禁用 if (opt.value <= selectedFrom) { opt.disabled = true; return; } // 检查选中区间内是否有已占用时段 let conflict = false; occupiedTimes.forEach(t => { if (t >= selectedFrom && t < opt.value) conflict = true; }); if (conflict) opt.disabled = true; }); });
结束时间下拉框的渲染逻辑和开始时间一致,遍历$to_options数组判断输出即可。
4. 后端二次校验(必须实现)
前端的禁用校验仅用于优化体验,无法阻挡恶意构造的请求,表单提交后必须在后端再次做冲突校验。可以直接通过SQL判断区间是否重叠,不需要查询全量记录:
SELECT COUNT(*) FROM reservations WHERE book_date = ? AND status = 'confirmed' AND start < ? AND end > ?
传入用户选定的日期、提交的结束时间、提交的开始时间三个参数,如果查询结果大于0,说明所选区间存在预订冲突,直接返回错误提示即可。
内容的提问来源于stack exchange,提问作者Ram
相关产品推荐
相关产品推荐

