cuatro patos

Creative Commons License photo credit: Gabriel FM

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.

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).
Edit: This is not entirely true. Look at Ricardo Almeida’s comment below.

//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: 41% [?]

Tagged with:
 

17 Responses to FullCalendar and CakePHP part 5 – Dragging and Resizing

  1. Ron Schatte says:

    I could not get ‘&gt’ to work in either page view or PHP controller.

    Is there option somewhere?
    Thanks,
    Ron

  2. Duck Ranger says:

    Sorry Ron – bad editing on my part.
    These are actually all the > character.
    I fixed the code samples now

  3. Ron Schatte says:

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

    }

  4. Sharon says:

    Thank you for publishing this! The calendar is great, and with this guide you’ve made it very easy to integrate.

  5. luca says:

    Hi, you can post a dowload link for this project? Very thank’s…

  6. Duck Ranger says:

    Hi Luca -
    Do you mean for fullcalendar or cakephp?

  7. luca says:

    Cake…

  8. Duck Ranger says:

    Cake PHP
    I am using the 1.2.x version – but it should work fine with 1.3.

    Good luck

  9. Luca says:

    sorry, I meant the link for the download of your project fullcaledar. thanks for the speed.

  10. Duck Ranger says:

    @luca – fullcalendar is not my project- it’s by Adam Shaw over at fullcalendar’s website

    Cheers

  11. Luca says:

    ok, thanks. I thought I had a way to send multiple files (controllers, model …) for cake… :(

  12. Ricardo Almeida says:

    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

  13. Duck Ranger says:

    @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

  14. naxis says:

    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?

  15. Duck Ranger says:

    @naxis – yes. It is possible. You probably need to change dayclick function so that it plays nicely with lightbox

  16. Lev says:

    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

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Set your Twitter account name in your settings to use the TwitterBar Section.