Недавно возникла потребность создать календарь событий, где каждая дата в календаре будет подсвечена ссылкой, если какое-нибудь событие присутствует для каждого числа. Если мне разрешат оставить ссылку, здесь демонстрация работы календаря.
Задача вроде бы не сложная, но среди немногочисленных решений в интернете я не нашел подходящего по следующим причинам: слишком сложный и непонятный код, медленные запросы к БД (это особенно ощущается, если в базе много записей), использование библиотеки jQuery, к которой я отношусь не очень хорошо.
Итак, к плюсам моего календаря можно отнесли следующее:
Теперь обо всем по-порядку.
Календарь генерируется средствами php для текущего месяца. Для каждого дня проверяем нет ли записей в БД, если есть, — формируем ссылку на событие. Дописываем javascript код для перелистывание месяцев, который обращается к скрипту через ajax. Задача усложняется тем, что события растянуты во времени, то есть, начинаются в один день, а заканчиваются через несколько дней или даже месяцев. На всем временном промежутке существование события нужно его подсветить ссылкой для каждого дня.
Таким образом, мы выбрали все записи, которые есть в текущем месяце.
Дальше самое интересное: заполняем обходочный массив. Для того, чтобы не крутить лишний раз все заново, если находится соответствие, элемент массива удаляется и следующий цикл имеет меньше итераций.
Собственно, сам календарь:
Он немного упрощен для наглядности (отсутствуют эффекты скольжения):
Закрываем тег таблицы:
Таким образом получился простой и легко встраиваемый календарь событий, который быстро работает и легко настраивается, работающий на чистом PHP+javascript без дополнительных библиотек.
Задача вроде бы не сложная, но среди немногочисленных решений в интернете я не нашел подходящего по следующим причинам: слишком сложный и непонятный код, медленные запросы к БД (это особенно ощущается, если в базе много записей), использование библиотеки jQuery, к которой я отношусь не очень хорошо.
Итак, к плюсам моего календаря можно отнесли следующее:
- Весь код помещается в 200 строчек и состоит из одного файла, который подключается через include
- Скрипт состоит из чистого php + javascript без использования библиотеки jQuery
- Используются простые и оптимизированные запросы к БД
- Подгрузка следующего (предыдущего) месяца происходит через AJAX
Теперь обо всем по-порядку.
Логика
Календарь генерируется средствами php для текущего месяца. Для каждого дня проверяем нет ли записей в БД, если есть, — формируем ссылку на событие. Дописываем javascript код для перелистывание месяцев, который обращается к скрипту через ajax. Задача усложняется тем, что события растянуты во времени, то есть, начинаются в один день, а заканчиваются через несколько дней или даже месяцев. На всем временном промежутке существование события нужно его подсветить ссылкой для каждого дня.
Генерируем календарь на PHP
<?php //Проверяем, пришел ли запрос на конкретную дату. Если нет, берем текущую дату. if (isset($_REQUEST['date'])) { //Проверяем, не пришло ли чего лишнего... $pattern = "/^([0-9]{4})-([0-9]{2})-([0-9]{2})$/"; if (preg_match($pattern, $_REQUEST['date'])) { $date = $_REQUEST['date']; } else { die('Неправильный параметр'); } } else { $date=date("Y-m-d"); } $sd = explode("-", $date); $year = $sd[0]; $month = $sd[1]; $day = $sd[2]; // Вычисляем число дней в текущем месяце $dayofmonth = date('t', mktime(0, 0, 0, $month, 1, $year)); //Готовим запрос к БД $todate = "$year-$month-$dayofmonth"; $fromdate = "$year-$month-01"; $query = "SELECT date,enddate from sobytia WHERE startdate<='$todate' AND enddate>=$fromdate"; $res_db = $db->sql($query);
Таким образом, мы выбрали все записи, которые есть в текущем месяце.
Дальше самое интересное: заполняем обходочный массив. Для того, чтобы не крутить лишний раз все заново, если находится соответствие, элемент массива удаляется и следующий цикл имеет меньше итераций.
$d = array();$k=array(); for($i = 1; $i<=$dayofmonth; $i++){ $k[$i] = $i; } $i=0; while ($a = mysqli_fetch_row($res_db)) { //for($i = 1; $i<=$dayofmonth; $i++){ foreach ($k as $i) { //Добавление 0 к дате if($i<10) $cd = "$year-$month-0".$i; else $cd = "$year-$month-$i"; if ($cd >= $a[0] && $cd <= $a[1]) { $d[$i] = $cd; unset($k[$i]); } } }
Собственно, сам календарь:
// Счётчик для дней месяца $day_count = 1; // 1. Первая неделя $num = 0; for($i = 0; $i < 7; $i++) { // Вычисляем номер дня недели для числа $dayofweek = date('w', mktime(0, 0, 0, $month, $day_count, $year)); // Приводим к числа к формату 1 - понедельник, ..., 6 - суббота $dayofweek = $dayofweek - 1; if($dayofweek == -1) $dayofweek = 6; if($dayofweek == $i) { // Если дни недели совпадают, // заполняем массив $week // числами месяца $week[$num][$i] = $day_count; $day_count++; } else { $week[$num][$i] = ""; } } // 2. Последующие недели месяца while(true) { $num++; for($i = 0; $i < 7; $i++) { $week[$num][$i] = $day_count; $day_count++; // Если достигли конца месяца - выходим // из цикла if($day_count > $dayofmonth) break; } // Если достигли конца месяца - выходим // из цикла if($day_count > $dayofmonth) break; } // 3. Выводим содержимое массива $week // в виде календаря // Выводим таблицу echo '<table id="calendar">'; //заголовок $rusdays = array('ПН','ВТ','СР','ЧТ','ПТ','СБ','ВС'); $rusmonth = array('Январь','Февраль','Март','Апрель','Май','Июнь','Июль','Август','Сентябрь','Октябрь','Ноябрь','Декабрь'); echo '<thead> <tr> <td onclick="monthf(\'prev\');"><</td> <td colspan="5">'.$rusmonth[$month-1].', '.$year.'</td> <td onclick="monthf(\'next\');">></td> </tr>'; echo '<tr>'; foreach ($rusdays as $rusday){ echo '<td>'.$rusday.'</td>'; } echo '</tr>'; echo '</thead>'; //тело календаря for($i = 0; $i < count($week); $i++) { echo "<tr>"; for($j = 0; $j < 7; $j++) { if(!empty($week[$i][$j])) { // Если имеем дело с выбраной датой подсвечиваем ee if($week[$i][$j]==$day) { echo '<td class="today">'; } else { echo '<td>'; } // Если запись в базе за текущую дату есть, делаем ссылку if($d[$week[$i][$j]]) { echo '<a href="/afisha/'.$d[$week[$i][$j]].'/">'.$week[$i][$j].'</a>'; } else { echo $week[$i][$j]; } echo '</td>'; } else echo "<td> </td>"; } echo "</tr>"; } ?>
Javascript код для перематывание месяцев
Он немного упрощен для наглядности (отсутствуют эффекты скольжения):
<script> var mon = parseInt("<?php echo $month; ?>"); var year = parseInt(<?php echo $year; ?>); function monthf(pn){ if (pn == 'next'){ mon++; }else if (pn == 'prev'){ mon--; }else{ alert('Неправильный параметр'); return false; } if (mon > 12){ year ++; mon = 1; } if (mon < 1){ year --; mon = 12; } if ((mon < 10) && (mon >= 1)){ mon = '0'+mon; } var nextDate = year+'-'+mon+'-00'; var ajaxaddr = "путь_к_текущему_скрипту?date='+nextDate; var http = new XMLHttpRequest(); if (http) { http.open('get', ajaxaddr); http.onreadystatechange = function () { if(http.readyState == 4){ if (http.status == 200) { document.getElementById('calendar').innerHTML = http.responseText; } } } http.send(null); } } </script>
Закрываем тег таблицы:
<?php echo "</table>"; ?>
Выводы
Таким образом получился простой и легко встраиваемый календарь событий, который быстро работает и легко настраивается, работающий на чистом PHP+javascript без дополнительных библиотек.
