Я пытаюсь написать календарь в PHP. В режиме недели я хочу, чтобы мои события были перечислены как iCal, где одновременные события уменьшают их ширину до половины размера.
Мне очень сложно понять, что это такое, поэтому я надеюсь, что вы можете мне помочь. Я хочу, чтобы, если одно событие перекрывает другое, оно должно установить [split] => true
на обоих массивах событий – или что-то в этом направлении (читайте: я не уверен, является ли это наиболее эффективным решением). Затем я могу проверить split == true
в цикле foreach, который печатает события.
Вот пример массива, содержащий два одновременных события:
$events = array( array( "id" => 21, "start" => 1242219600, "end" => 1242237600, "title" => "foo", "split" => false ), array( "id" => 22, "start" => 1242223200, "end" => 1242234000, "title" => "foo", "split" => false ) ); $events = someFunctionToOffsetEvents($events);
Как бы вы решили этот?
В последнее время мне приходилось сталкиваться с проблемами столкновения с датами, и лучшее, что я смог придумать, это:
date1.start <date2.end и date1.end> date2.start = collision
Эта простая формула будет учитывать все следующие ситуации:
- для всех ситуаций: date1.start = 1/1/2009 date1.end = 1/10/2009 - Дата начала в первом диапазоне дат: date2.start = 1/9/2009 date2.end = 2/10/2009 - Дата указана в первом диапазоне дат: date2.start = 12/10/2008 date2.end = 1/3/2009 - Дата начала и окончания внутри первого диапазона дат: date2.start = 1/2/2009 date2.end = 1/3/2009 - Первая дата находится во втором диапазоне дат: date2.start = 12/1/2008 date2.end = 2/1/2009
$date1 = array('start' => '2009-01-05', 'end' => '2009-01-10'); $date2 = array('start' => '2009-01-01', 'end' => '2009-01-04'); // end inside one $date3 = array('start' => '2009-01-04', 'end' => '2009-01-15'); // start inside one $date4 = array('start' => '2009-01-01', 'end' => '2009-01-15'); // one inside me $date5 = array('start' => '2009-01-04', 'end' => '2009-01-05'); // inside one function datesCollide($date1, $date2) { $start1TS = strtotime($date1['start']); $end1TS = strtotime($date1['end']); $start2TS = strtotime($date2['start']); $end2TS = strtotime($date2['end']); if ($start1TS <= $end2TS && $end1TS >= $start2) { return true; } return false; }
На основе вашего комментария это, вероятно, решение, которое вы ищете: обратите внимание, что это решение не очень оптимизировано и должно использоваться только для определения лучшего решения. Никогда не доверяйте коду из Интернета.
$events = array( array( "id" => 21, "start" => 1242219600, "end" => 1242237600, "title" => "foo", "split" => false ), array( "id" => 22, "start" => 1242223200, "end" => 1242234000, "title" => "foo", "split" => false ) ); foreach ($events as $key => $event) { $events[$key]->split = dateCollisionCheck($event, $events); } function dateCollisionCheck(&$event, &$eventList) { foreach ($eventList as $checkEvent) { if ($event->id != $checkEvent->id) { if ($event->start <= $checkEvent->end && $event->end >= $checkEvent->start) { return true; // return as soon as we know there is a collision } } } return false; }
* Код не был протестирован
Я разместил вопрос о том, что, если три события перекрываются, что приводит к лучшему решению. Не помещайте свой графический интерфейс и ваши данные в одну структуру. Сохраните данные, а затем выясните, как их отображать.
Я бы отменил вашу проблему. Сопоставьте события в массиве, представляющие неделю, которую вы хотите отобразить:
$weekdayEvents=array(); foreach ($events as $event) { $day=toThisWeek($event['start'], $firstweekday), $lastDay=toThisWeek($event['end'], $firstweekday); while ($day<=$lastDay) { $weekdayEvents[$day++][]=$event['id']; } } // now you have an array of array filled with ids $eventCollisions=array(); foreach($weekdaysEvents as $activeDay) { $eventsOnDay = count($activeDay); foreach($activeDay as $eventID) { if ($eventCollisions[$eventID]<$eventsOnDay) { $eventCollisions[$eventID]=$eventsOnDay; } } }
с$weekdayEvents=array(); foreach ($events as $event) { $day=toThisWeek($event['start'], $firstweekday), $lastDay=toThisWeek($event['end'], $firstweekday); while ($day<=$lastDay) { $weekdayEvents[$day++][]=$event['id']; } } // now you have an array of array filled with ids $eventCollisions=array(); foreach($weekdaysEvents as $activeDay) { $eventsOnDay = count($activeDay); foreach($activeDay as $eventID) { if ($eventCollisions[$eventID]<$eventsOnDay) { $eventCollisions[$eventID]=$eventsOnDay; } } }
}
где toThisWeek () – это функция, подобная
function toThisWeek($dayTimestamp, $firstDayOfWeekTimestamp) { $dayOfWeek = unixtojd($firsDayOfWeekTimestamp) - unixtojd($dayTimestamp); if($dayOfWeek<0) return 0; if($dayOfWeek>6) return 6; return $dayOfWeek; }
Теперь в $ eventCollisions у вас есть максимальное количество столкновений в событии для каждого события, происходящего в течение недели, которую вы должны отображать. Теперь вы можете использовать значения в этом массиве для заполнения разделения полей в субаре $ events:
foreach ($events as $event) { $event['split']=$eventCollisions[$event['id']]-1; }
Или просто используйте значения в $ eventCollisions "ondemand"
Вот рабочий, измененный код, основанный на ответе Роба:
// this is in my getEvents function if(is_array($events)) { foreach ($events as $key => $event) { $events[$key]['collides'] = $this->dateCollisionCheck($event, $events); } } //this is another function added to calendar.class.php function dateCollisionCheck(&$event, &$eventList) { foreach ($eventList as $checkEvent) { if ($event['id'] != $checkEvent['id']) { if ($event['start'] <= $checkEvent['end'] && $event['end'] >= $checkEvent['start']) { return true; } } } return false; }