In 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.
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.
The code in calendar.ctp looks like this:
...
$('#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):
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:
- Locate the event from the database. I didn’t add defensive code for cases when the event id is not found.
- 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.
- 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.
- Save the event with the new start and end data
- 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:
...
$('#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).
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:
- Part 1: Set up
- Part 2: Creating an event source
- Part 3: Adding events
- Part 4: Editing events
- Part 5: Dragging and resizing
Popularity: 48% [?]


I could not get ‘>’ to work in either page view or PHP controller.
Is there option somewhere?
Thanks,
Ron
Sorry Ron – bad editing on my part.
These are actually all the > character.
I fixed the code samples now
Thanks!!!
I see this in other code.
Now I see that > is just >
Hey, I found something on another board that seems to work as alternate way of keeping trach of the event location. Now, I have my version which handles drag and resize.
—————————————————————–
function movea($id=null){
$firephp = FirePHP::getInstance(true);
$this->Event->id = $this->params['named']['id'];
$firephp->log($this->Event->id, ‘movea, $this->Event->id’);
// Use of the params to get the values.
$firephp->log($this->params['named']['start'], ‘movea, this->params start’);
$firephp->log($this->params['named']['end'], ‘movea, this->params end’);
$this->Event->saveField(‘start’,$this->params['named']['start']);
$this->Event->saveField(‘end’,$this->params['named']['end']);
$firephp->log($this->Event, ‘movea, this->Event’);
}
———————————————————
eventDrop: function(event) { // Make sure to read the plugin’s documentation
var dt = new Date(event.start); // event.start is the new date where you dragged and dropped the event post.
var yr = dt.getFullYear();
var dy = dt.getDate();
var mth = dt.getMonth()+1;
var hrs = dt.getHours();
var mns = dt.getMinutes();
var newdate = yr+’-'+mth+’-'+dy+’ ‘+hrs+’:'+mns+’:00′; // pass this date to the database via post.
var dt0 = new Date(event.end); // event.end is the new date where you dragged and dropped the event post.
var yr0 = dt0.getFullYear();
var dy0 = dt0.getDate();
var mth0 = dt0.getMonth()+1;
var hrs0 = dt0.getHours();
var mns0 = dt0.getMinutes();
var newdate0 = yr0+’-'+mth0+’-'+dy0+’ ‘+hrs0+’:'+mns0+’:00′; // pass this date to the database via post.
$.post(“/events/movea/id:”+event.id+”/start:”+newdate+”/end:”+newdate0);
},
eventResize: function(event) { // Make sure to read the plugin’s documentation
var dt = new Date(event.start); // event.start is the new date where you dragged and dropped the event post.
var yr = dt.getFullYear();
var dy = dt.getDate();
var mth = dt.getMonth()+1;
var hrs = dt.getHours();
var mns = dt.getMinutes();
var newdate = yr+’-'+mth+’-'+dy+’ ‘+hrs+’:'+mns+’:00′; // pass this date to the database via post.
var dt0 = new Date(event.end); // event.end is the new date where you dragged and dropped the event post.
var yr0 = dt0.getFullYear();
var dy0 = dt0.getDate();
var mth0 = dt0.getMonth()+1;
var hrs0 = dt0.getHours();
var mns0 = dt0.getMinutes();
var newdate0 = yr0+’-'+mth0+’-'+dy0+’ ‘+hrs0+’:'+mns0+’:00′; // pass this date to the database via post.
$.post(“/events/movea/id:”+event.id+”/start:”+newdate+”/end:”+newdate0);
}