You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 16:34:13