FullCalendar and CakePHP part 5 – Dragging and Resizing

Drag and resize queensIn the previous part of this series we saw how to edit fullCalendar events via CakePHP.
In this post we’ll see how to let users resize or drag’n'drop an event displayed on the screen.

Not every change to an event in fullCalendar requires a lot of user interaction. If, for example, your user wants to move an event to a different day, or to a different hour in the same day, all she has to do is click to drag’n'drop the event to its new location.

fullcalendar with some events

FullCalendar

If, on the other hand, the user wants to change the end time for the event, but keep its start-time unchanged – all she has to do is resize the event on the screen, taking the bottom edge to the new timeslot.

As you may have guessed, fullCalendar blesses us with two events to handle these situations:

eventDrop

eventDrop is fired when the user has (dragged and) dropped an event on its new timeslot. FullCalendar supplies the event handler with the event that was dropped, along with the day and minute deltas (that is – the change in days and minutes of the event’s start time).
In the code below you can see that fullCalendar also passes the ‘allDay’ flag. This is useful if an event was dropped on the ‘All day’ slot of the calendar. The CakePHP controller code deals with this flag appropriately.

For example – if the user dragged and dropped the Lunch at High Noon event to the following location (moved from Wednesday to Friday, but still at noon) – the event handler receives the event object describing the lunch at High Noon, together with a delta of +2 days and 0 minutes.

events after the move

The code in calendar.ctp looks like this:

$(document).ready(function() {
...
$('#calendar').fullCalendar({
...
eventDrop: function(event,dayDelta,minuteDelta,allDay,revertFunc) {
if (dayDelta>=0) {
dayDelta = "+"+dayDelta;
}
if (minuteDelta>=0) {
minuteDelta="+"+minuteDelta;
}
$.post("/events/move/"+event.id+"/"+dayDelta+"/"+minuteDelta+"/"+allDay);
},
...
}
}

The event handler ensures that the delta values are sent to the controller using a signed notation. This is not necessary – it just makes life easier in the controller.
All the controller has to do is retrieve the event using the event id sent in the POST request, change its start time according to the deltas (and possibly change its allDay status):

//Inside events_controller.php
function move ($id=null,$dayDelta,$minDelta,$allDay) {
if ($id!=null) {
$ev = $this->Event->findById($id);  //1 - locate the event in the DB
if ($allDay=='true') { //2- handle all day events
$ev['Event']['allday'] = 1;
} else {
$ev['Event']['allday'] = 0;
}
//3 - Start
$ev['Event']['end']=date('Y-m-d H:i:s',strtotime(''.$dayDelta.' days '.$minDelta.' minutes',strtotime($ev['Event']['end'])));
$ev['Event']['start']=date('Y-m-d H:i:s',strtotime(''.$dayDelta.' days '.$minDelta.' minutes',strtotime($ev['Event']['start'])));

$this->Event->save($ev); //4 - Save the event with the new data
//5 - redirect and reload
$this->redirect(array('controller' => "events", 'action' => "calendar",substr($ev['Event']['start'],0,4),substr($ev['Event']['start'],5,2),substr($ev['Event']['start'],8,2)));
}
}

The controller does the following:

  1. Locate the event from the database. I didn’t add defensive code for cases when the event id is not found.
  2. Handle the possible dropping of the event on the all-day slot (or dragging it out from there). This is done by setting the event’s allday status in the DB.
  3. These two lines handle the change of the start and end time of the event. When the user drag’n'drops an event, she doesn’t change the event’s duration – only the start and end time move by the delta of days and minutes that fullCalendar event handler passes to the move method in the POST request.

    Since the event handler ensured that the delta is signed (that is – always prefixed by a plus or minus sign) – we can use strtotime date operations with the date and delta.

    If, for example, the deltas passed are +2 days and +0 minutes, the lines that assign new values to the Event’s start and end dates will actually be:

    $ev['Event']['end']=date('Y-m-d H:i:s',strtotime('+2 days +0 minutes',strtotime($ev['Event']['end'])));
    $ev['Event']['start']=date('Y-m-d H:i:s',strtotime('+2 days +0 minutes',strtotime($ev['Event']['start'])));

    Which means theat both dates get 2 days added to them.

  4. Save the event with the new start and end data
  5. Redirect to calendar.ctp, passing the year, month and day of the event as parameters.
    Passing these parameters make calendar.ctp load the correct timeframe, so that the event just dropped is displayed on the screen. (See Editing events for an explanation).

Resizing an event

Resizing an event is even easier than dragging. FullCalendar lets you resize only from the bottom, meaning that a resize event will only change the end-time of an event, but will never move its start time.

The event handler in calendar.ctp looks like this:

$(document).ready(function() {
...
$('#calendar').fullCalendar({
...
eventResize: function(event,dayDelta,minuteDelta,revertFunc) {
if (dayDelta>=0) {
dayDelta = "+"+dayDelta;
}
if (minuteDelta>=0) {
minuteDelta="+"+minuteDelta;
}
$.post("/events/resize/"+event.id+"/"+dayDelta+"/"+minuteDelta);
},
...
}
}

Similar to the eventDrop handler, the code passes deltas to the handler. Unlike the eventDrop handler, these deltas relate the change to the end time of the event – not the start time.

Like the eventDrop handler, the code makes sure that the deltas are signed, and then makes a POST call to the controller. This time to the resize event, passing the event’s id, and the two deltas. (Note that in this handler, there’s no question of the event’s allDay status being changed).

//Inside events_controller.php
function resize ($id=null,$dayDelta,$minDelta) {
if ($id!=null) {
$ev = $this->Event->findById($id);
$ev['Event']['end']=date('Y-m-d H:i:s',strtotime(''.$dayDelta.' days '.$minDelta.' minutes',strtotime($ev['Event']['end'])));
$this->Event->save($ev);
}
$this->redirect(array('controller' => "events", 'action' => "calendar",substr($ev['Event']['start'],0,4),substr($ev['Event']['start'],5,2),substr($ev['Event']['start'],8,2)));
}

The method finds the relevant event in the database, and adds the deltas to its end time. This is because only the end time of an event changes as a result of a resize event.

That’s it really… this concludes the series on integrating fullCalendar and CakePHP!

The fullCalendar and CakePHP Series:

  1. Part 1: Set up
  2. Part 2: Creating an event source
  3. Part 3: Adding events
  4. Part 4: Editing events
  5. Part 5: Dragging and resizing

Popularity: 48% [?]

About the Author