views:

414

answers:

4

Greetings everyone!

I am trying to write a calendar in PHP. In week view, I want my events to be listed like iCal, where simultaneous events reduces their width to half size.

I have an extremely hard time figuring this one out though, so I hope you can help me. What I want is that if one event is overlapping another, it should set "[split] => true" on both event arrays - or something in that direction (read: I am unsure whether this is the most efficient solution). Then I can check for split == true in the foreach loop which prints out the events.

Here is an example array containing two simultaneous events:

$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);

How would you solve this one?

Thanks guys!

+1  A: 

I posted the question about what if three events overlap, which leads to a better solution. Don't put your GUI and your data in the same structure. Save your data, and then figure out how to display it.

thursdaysgeek
Can you paste a link to that question/solution? Thanks :-)
Preben
Sorry. That was just referring to the comment above, to your question.
thursdaysgeek
I was just saying that you need to worry about not just two events colliding, but three or more. So, you really need to first just store your data, and then have a separate object for displaying data, and dealing with any time collisions during the draw process. Break your problem down so that the data and the display are completely separate. Moreover, you can then use the data for more than just that one display, and that display with more than just that set of data.
thursdaysgeek
I completely agree with thrusdaysgeek. Clear separation between your data and display will be a gift that keeps on giving as you go forward with your project.
Rob Booth
A: 
Rob Booth
Hi Rob. Thanks for your suggestion. What is different from my code to yours, is that I have an array wrapped around all of my events, which requires my "datesCollide" function to iterate through the entire multi-dimensional array. How would you do that? I think I spent so much time on this one, that it made me kind of blind.
Preben
So you have 1 array with X events and you have to check if any of them collide with each other?
Rob Booth
I have a function getEvents($start, $end), which pulls the events only for the week I am interested in seeing. Therefore I actually think your solution is as optimized as I can get it. I cannot see how I could do it in a better way. I had to modify your code a bit to make it work. I will post it in the bottom. But you get the solution award. Thank you very much for your help!
Preben
A: 

I would revert your problem. Map the events on an array representing the week you want to display:

$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;
    }
  }
}

}

where toThisWeek() is a function like

function toThisWeek($dayTimestamp, $firstDayOfWeekTimestamp) {
  $dayOfWeek = unixtojd($firsDayOfWeekTimestamp) - unixtojd($dayTimestamp);
  if($dayOfWeek<0) return 0;
  if($dayOfWeek>6) return 6; 
  return $dayOfWeek;      
}

Now in $eventCollisions you have the max number of collision occur on an event for every events occurring during the week you have to display. Now you can use the values in this array to fill the field split in the $events subarray:

foreach ($events as $event) {
  $event['split']=$eventCollisions[$event['id']]-1;
}

Or just use the values in $eventCollisions "ondemand"

Eineki
A: 

Here is the working, modified code based on Rob's answer:

// 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;
}
Preben