FullCalendar and CakePHP part 5 – Dragging and Resizing
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).
Edit: This is not entirely true. Look at Ricardo Almeida’s comment below.
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: 41% [?]
17 Responses to FullCalendar and CakePHP part 5 – Dragging and Resizing
Leave a Reply Cancel reply
Categories
- Android (1)
- appengine (3)
- Be nice to your users (6)
- CakePHP (13)
- General (6)
- Googlemaps (3)
- Java (16)
- javaScript (5)
- jQuery (11)
- Seam (2)
- Security (5)
- Spring MVC (6)
- Spring Webflow (2)
- Ubuntu (1)
- User Interface (6)
- Wordpress (2)
- Zope (2)
Recent Comments
- paskuale: @Duck Ranger framework’s name: Yii (Yes it is)
- Hanzel: I’m having a hard time understanding why when i click “run as…” in the project it...
- paskuale: Great article, thanks to your article I realized the same thing but with another framework, I don’t...
- Radix: This indeed came first in google and saved heck lot of time for me. Thanks
- Aditya: Spot on!! Thank you!! Had no idea why this error was occurring.
- paskuale: @Duck Ranger framework’s name: Yii (Yes it is)
Archives
- April 2012 (5)
- March 2012 (1)
- February 2012 (1)
- January 2012 (2)
- December 2011 (1)
- July 2011 (2)
- June 2011 (4)
- May 2011 (1)
- December 2010 (3)
- November 2010 (1)
- August 2010 (2)
- July 2010 (2)
- June 2010 (4)
- May 2010 (4)
- April 2010 (5)
- March 2010 (3)
Growing Kiwis
Growing Kiwis is my free service for early childhood education centers, preschools, daycares, kindies and more- any group of children, really.
Let me know what you think!













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);
}
Thank you for publishing this! The calendar is great, and with this guide you’ve made it very easy to integrate.
Hi, you can post a dowload link for this project? Very thank’s…
Hi Luca -
Do you mean for fullcalendar or cakephp?
Cake…
Cake PHP
I am using the 1.2.x version – but it should work fine with 1.3.
Good luck
sorry, I meant the link for the download of your project fullcaledar. thanks for the speed.
@luca – fullcalendar is not my project- it’s by Adam Shaw over at fullcalendar’s website
Cheers
ok, thanks. I thought I had a way to send multiple files (controllers, model …) for cake…
Congratulations for another useful chapter on this topic. I just don’t understand why you say “there’s no question of the event’s allDay status being changed”, when refering to resizing an event. If I pull down the end date of an event which doesn’t start at midnight, the allDay will remain 0. If it starts at midnight and ends before the midnight of the next day but I pull the end date to midnight, the allDay changes from 0 to 1.
Thanks
@Ricardo – I think I can see what you mean. In my examples, I don’t show how to create multi-days events. So – no matter how you drag and drop, the event always remains within the same day.
I assume that you let your users create multi-days events, and in this case, yes – the situation you describe is possible, and you should add the code to do that in the controller.
Cheers
Thank you for the work.
I’m having some problems.Is it possible to show the event adding form in a light box like in the demos?
@naxis – yes. It is possible. You probably need to change dayclick function so that it plays nicely with lightbox
For some reason when I try to drag&drop events (not external) the events are not being dropped. Instead they are reverted to the original placement.
I checked that ‘editable’ is set on both event and calendar level but still can’t “drop” the event at the new place.
Any ideas what can cause this?
Regards,
Lev
@Lev – have a look here: http://stackoverflow.com/questions/8469161/drop-doesnt-work-in-fullcalendar