In the previous part of this series we saw how to link fullCalendar via CakePHP to a MySQL events table. This is nice, but it’s only really useful if all the Create/Update and Delete actions on your events happen somewhere else.
Obviously – you’ll want a way to manipulate events directly from the calendar control on the screen.
FullCalendar has excellent support for event manipulation. In this post we’ll see one way to integrate this with CakePHP, to create a full-fledged CRUD calendar in your application.
A calendar control is quite confusing when interactive applications are concerned. This is because the term ‘event’ has two meanings in this context. One type of events makes up the calendar’s data, while the other type is fired when the user clicks an area on the calendar control.
FullCalendar calls both of these types ‘events’ – which makes the (extensive and impressive) documentation a bit confusing at times. This just means you need to keep focus when reading it.
FullCalendar exposes a few event handles for you. I am going to deal with the following:
- dayClick: function(date, allDay, jsEvent, view) This is the one used for adding events, which we cover in this post
- eventClick: function(calEvent, jsEvent, view)
- eventDrop: function(event,dayDelta,minuteDelta,allDay,revertFunc)
- eventResize: function(event,dayDelta,minuteDelta,revertFunc)
The way this works is quite simple. When one of these events happen, fullCalendar calls the event handler you attach to that event. The event handler receives the parameters in the list above, and all you need to do is write the code to parse and handle the data. (For full details of these parameters – see fullCalendar’s documentation).
The dayClick handle is called when a day is clicked in the calendar. The data that interests us in our event handler is the date object and the allDay flag. We can use this data to initiate an add operation in our events_controller.
A nice way to implement this is by presenting the user with a simple form to add the event details. This form can be displayed in various ways – you can use lightbox, thickbox, fancybox – any box, really. In this example I’ll just use a simple <div> element. It is normally hidden, and is shown only when the add (and later edit) operation is called:
Inside the fullCalendar() init method, add this:
$("#eventdata").show();
$("#eventdata").load("<?php echo Dispatcher::baseUrl();?>/events/add/"+allDay+"/"+$.fullCalendar.formatDate( date, "dd/MM/yyyy/HH/mm"));
},
What’s happening here?
Simple, really. We define a function for the dayClick event. This function is called by fullCalendar whenever the user clicks an empty slot on the calendar.
The first thing this function does is call jQuery‘s show() on the eventdata <div> element:
Using the eventdata div is simple. Style and position it with CSS, and use the following code to define and hide it automatically:
<!-- hide the eventdata div when the page loads -->
<script type="text/javascript">
$(document).ready(function(){
$("#eventdata").hide();
...
}
</script>
After show()-ing the eventdata div, our event handler loads its content using an ajax request to the add() method on our events_controller.
The request parameters are the value of allDay, along with the date/time slot clicked on the calendar. We use fullCalendar’s formatDate utility to split the date object into a dd/MM/yyyy/HH/mm fromat. This results in passing a total of 6 parameters to the add method.
Clicking on the slot for 09:00 at March 26, 2010 will submit the following ajax request:
The add() method in the events_controller receives this request and responds with a form that lets the user create the event.
You can let the user enter the event name, and maybe change the event’s start and end date/time. The sample below is very simplistic, and only asks the user for the event’s title. It defaults to a 1-hour long event, starting at the timeslot the user originally clicked:
if (empty($this->data)) {
//Set default duration: 1hr and format to a leading zero.
$hourPlus=intval($hour)+1;
if (strlen($hourPlus)==1) {
$hourPlus = '0'.$hourPlus;
}
//Create a time string to display in view. The time string
//is either "Fri 26 / Mar, 09 : 00 — 10 : 00" or
//"All day event: (Fri 26 / Mar)"
if ($allday=='true') {
$event['Event']['allday'] = 1;
$displayTime = 'All day event: ('
. date('D',strtotime($day.'/'.$month.'/'.$year)).' '.
$day.' / '. date("M", mktime(0, 0, 0, $month, 10)).')';
} else {
$event['Event']['allday'] = 0;
$displayTime = date('D',strtotime($day.'/'.$month.'/'.$year)).' '
.$day.' / '.date("M", mktime(0, 0, 0, $month, 10)).
', '.$hour.' : '.$min.' — '.$hourPlus.' : '.$min;
}
$this->set("displayTime",$displayTime);
//Populate the event fields for the add form
$event['Event']['title'] = 'Event description';
$event['Event']['start'] = $year.'-'.$month.'-'.$day.' '.$hour.':'.$min.':00';
$event['Event']['end'] = $year.'-'.$month.'-'.$day.' '.$hourPlus.':'.$min.':00';
$this->set('event',$event);
//Do not use a view template.
$this->layout="empty";
} else {
//Create and save the new event in the table.
//Event type is set to editable - because this is a user event.
$this->Event->create();
$this->data['Event']['title'] = Sanitize::paranoid($this->data["Event"]["title"], array('!','\'','?','_','.',' ','-'));
$this->data['Event']['editable']='1';
$this->Event->save($this->data);
$this->redirect(array('controller' => "events", 'action' => "index");
}
}
The method’s functionality is simple: in the spirit of CakePHP – if $this->data is empty (i.e. this is the initial ajax load request, not the form submit) – the method sets up an Event object and passes it to the view.
If, on the other hand, $this->data contains information – the method takes it to be the result of a form submit, and saves the newly created event in the database.
Note that the method only sanitizes the event title it receives from the user and not the other fields. This is because the other fields are either too small to let a malicious user script (size=1) or are never re-displayed to the user. So, even if someone spoofs your form – no harm will come your way (of course, if you want to feel safe – you are welcome to sanitize the rest of the fields).
All that’s left now is create the view for the add() method. This is a very simple view (remember – it is used only to populate the eventdata div):
<?php
echo $form->create('Event', array('target'=> '_parent'));
echo $form->input('title' , array('label' => 'Event title'));
echo '<br/>At: ' . $displayTime;
echo $form->input('start', array('type'=>'hidden','value'=>$event['Event']['start']));
echo $form->input('end', array('type'=>'hidden','value'=>$event['Event']['end']));
echo $form->input('allday', array('type'=>'hidden','value'=>$event['Event']['allday']));
echo $form->end(array('label'=>'Save' ,'name' => 'save'));
?>
If you’d like to be nicer to your users – you can add the following at the end of add.ctp:
<script type="text/javascript">
function back() {
window.location.href ="/events/index";
}
</script>
That’s it, really. When the user submits the form, a HTTP POST request is fired to the add() method on the events_controller.
When the method receives the POST, $this->data is not empty, and therefore the method creates and saves the new event in the table.
When you return to the calendar view (the last line in the add() method redirects to /events/index) – the new event will appear there, since it is now loaded from the json event source we created in the previous post.
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: 75% [?]