Календарь, смещение перекрывающихся событий - PullRequest
2 голосов
/ 21 мая 2009

Я пытаюсь написать календарь на 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);

Как бы вы решили это?

Ответы [ 4 ]

1 голос
/ 21 мая 2009

В последнее время мне приходилось сталкиваться с проблемами столкновения дат, и лучшее, что я смог придумать, это:

date1.start date2.start = столкновение

Эта простая формула будет учитывать все следующие ситуации:

--same for all situations:
date1.start = 1/1/2009
date1.end = 1/10/2009

--Start date is in first dates range:
date2.start = 1/9/2009
date2.end = 2/10/2009

--End date is in first dates range:
date2.start = 12/10/2008
date2.end = 1/3/2009

--Start & End date is inside first dates range:
date2.start = 1/2/2009
date2.end = 1/3/2009

--First date is inside of second dates range:
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;
}

* Код не был проверен

1 голос
/ 21 мая 2009

Я разместил вопрос о том, что если три события перекрываются, что приводит к лучшему решению. Не размещайте ваш графический интерфейс и ваши данные в одной структуре. Сохраните ваши данные, а затем выясните, как их отобразить.

0 голосов
/ 21 мая 2009

Вот рабочий, модифицированный код, основанный на ответе Роба:

// 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;
}
0 голосов
/ 21 мая 2009

Я бы решил твою проблему. Отобразите события в массиве, представляющем неделю, которую вы хотите отобразить:

$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"

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...