PHP日历开发:如何标记数据库中所有日期区间内的日期?
嘿,我明白你现在卡在哪了——你的日历只能标记数据库里最后一条预订记录的日期范围,之前的所有预订都没显示出来对吧?这问题根源很简单:你在遍历数据库查询结果的时候,每次都用新的$date_from和$date_to覆盖了旧值,循环结束后这两个变量只保留了最后一行的数据,自然只能处理最后一条记录的日期。
咱们来一步步修复这个问题,顺便实现所有占用日期的标记(还能扩展成不同记录用不同颜色):
第一步:先把所有预订的日期都收集到一个集合里
首先,我们需要把数据库里每一条预订的日期范围都拆解成单个日期,统一存到一个数组(或映射)里,而不是只保留最后一条。
修改你数据库查询后的代码部分:
// 初始化数组,用来存所有已被占用的日期(或日期与颜色的映射) $occupiedDates = []; // 数据库查询保持不变 $sql = "SELECT date_from, date_to FROM booking_2"; $result = $this->connect()->query($sql); if ($result->num_rows > 0) { while ($row = $result->fetch_assoc()) { // 把字符串日期转成DateTime对象,方便生成日期范围 $startDate = new DateTime($row['date_from']); $endDate = new DateTime($row['date_to']); // DatePeriod默认不包含结束日期,所以给结束日期加一天 $endDate->modify('+1 day'); // 生成这个预订的所有日期 $period = new DatePeriod($startDate, new DateInterval('P1D'), $endDate); foreach ($period as $date) { // 转成Y-m-d格式存入数组 $occupiedDates[] = $date->format('Y-m-d'); } } // 去重,避免同一日期被多次添加 $occupiedDates = array_unique($occupiedDates); }
第二步:渲染日历单元格时检查日期是否被占用
接下来,在循环生成日历每个日期单元格的时候,检查当前日期是否在$occupiedDates数组里,然后给对应的单元格加上样式:
找到你生成日期单元格的那段代码,替换成下面的逻辑:
<?php $dayCount = 1; for ($cb = 1; $cb <= $boxDisplay; $cb++) { if (($cb >= $currentMonthFirstDay || $currentMonthFirstDay == 7) && $cb <= ($totalDaysOfMonthDisplay - 1)) { $currentDate = $dateYear.'-'.$dateMonth.'-'.$dayCount; // 检查当前日期是否在已占用数组中 $isBooked = in_array($currentDate, $occupiedDates); // 设置单元格样式:如果被占用就加背景色 $cellStyle = $isBooked ? 'style="background-color:#FF3232 !important;"' : ''; // 输出单元格 echo '<li date="'.$currentDate.'" class="date_cell" '.$cellStyle.'>'; echo '<span>'.$dayCount.'</span>'; echo '</li>'; $dayCount++; } else { ?> <li><span> </span></li> <?php } } ?>
扩展:给不同的预订记录用不同颜色
如果想让每条预订的日期显示不同的颜色,我们可以把日期和对应颜色做个映射:
// 定义一组颜色,循环使用 $availableColors = ['#FF3232', '#32FF66', '#3266FF', '#FFCC32', '#CC32FF']; $colorIndex = 0; // 用关联数组存日期对应的颜色 $dateColorMap = []; $sql = "SELECT date_from, date_to FROM booking_2"; $result = $this->connect()->query($sql); if ($result->num_rows > 0) { while ($row = $result->fetch_assoc()) { $startDate = new DateTime($row['date_from']); $endDate = new DateTime($row['date_to']); $endDate->modify('+1 day'); // 给当前预订分配一个颜色 $currentColor = $availableColors[$colorIndex % count($availableColors)]; $period = new DatePeriod($startDate, new DateInterval('P1D'), $endDate); foreach ($period as $date) { $dateStr = $date->format('Y-m-d'); // 如果日期还没被分配颜色,就用当前预订的颜色(避免后预订的覆盖先预订的) if (!isset($dateColorMap[$dateStr])) { $dateColorMap[$dateStr] = $currentColor; } } $colorIndex++; } }
然后渲染单元格时改成:
$currentDate = $dateYear.'-'.$dateMonth.'-'.$dayCount; $cellStyle = ''; if (isset($dateColorMap[$currentDate])) { $cellStyle = 'style="background-color:'.$dateColorMap[$currentDate].' !important;"'; } echo '<li date="'.$currentDate.'" class="date_cell" '.$cellStyle.'>'; echo '<span>'.$dayCount.'</span>'; echo '</li>';
为什么原来的代码不行?
你之前的while循环里,每次都会把$date_from和$date_to替换成当前行的数据,循环结束后这两个变量只剩下最后一条记录的值,所以后面的DatePeriod只处理了最后一个预订的日期范围,前面的所有预订数据都被覆盖丢失了。
现在这样修改后,所有预订的日期都会被收集起来,渲染时逐个检查就能全部标记出来啦!
内容的提问来源于stack exchange,提问作者kaspianso
相关产品推荐
相关产品推荐

