Event calendars are a powerful tool that keeps track of upcoming events, schedules, appointments, and deadlines.
I am using FullCalendar plugin to create a calendar layout and make it dynamic using PHP and MySQL. Plugin is not dependent on any library, responsive, and automatically fits on the page. Different types of options and events are available to customize and control it.
In this tutorial, I show how you can create an event, update and delete an existing event, and load events in the FullCalendar JavaScript library using PHP AJAX and MySQL.
I am using FullCalendar v6 in the example and sweetAlert library to display an alert box to add/edit events.

Table of Content
- Create a Table
- Create a Database connection file
- Download and Include FullCalendar
- HTML – Setting Up the FullCalendar Container
- Create a PHP File to Load All Events in FullCalendar
- Create a PHP File to Manage AJAX Requests
- jQuery: Initializing FullCalendar and Managing Events
- Demo
- Conclusion
1. Create a Table
I am using events table in the example. It has the following structure –
CREATE TABLE `events` (
`id` int(10) NOT NULL,
`title` varchar(190) NOT NULL,
`description` text NOT NULL,
`start_date` date NOT NULL,
`end_date` date NOT NULL
);
ALTER TABLE `events`
ADD PRIMARY KEY (`id`);
ALTER TABLE `events`
MODIFY `id` int(10) NOT NULL AUTO_INCREMENT;
2. Create a Database connection file
Create a config.php for database configuration.
<?php
$host = "localhost"; /* Host name */
$user = "root"; /* User */
$password = ""; /* Password */
$dbname = "tutorial"; /* Database name */
$con = mysqli_connect($host, $user, $password,$dbname);
// Check connection
if (!$con) {
die("Connection failed: " . mysqli_connect_error());
}
3. Download and Include FullCalendar
- Download Fullcalendar and sweetAlert libraries.
- Also included a jQuery library to send AJAX requests.
<!-- jQuery --> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.3/jquery.min.js"></script> <!-- Fullcalendar --> <script type="text/javascript" src="fullcalendar-6.1.4/dist/index.global.min.js"></script> <!-- Sweetalert --> <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
4. HTML – Setting Up the FullCalendar Container
Creating an index.php file.
Within this file, use PHP to store the current date in a $currentData variable. This date will be used to set the starting point for FullCalendar.
Next, create <div id='calendar' > to initialize FullCalendar.
<?php
$currentData = date('Y-m-d');
?>
<!-- Calendar Container -->
<div id='calendar-container'>
<div id='calendar'></div>
</div>
5. Create a PHP File to Load All Events in FullCalendar
- Create
fetchevents.phpfile to load all created events list in FullCalendar. - Retrieve all event records from the
eventstable. This gathered information will be stored in the$eventsListvariable. - Loop through the
$eventsList. During each iteration, gather key event details such as ‘eventid’, ‘title’, ‘description’, ‘start’, and ‘end’ dates. These details will be compiled into a convenient$responsearray. - To ensure seamless integration with FullCalendar, convert the
$responsearray into JSON format.
<?php
include "config.php";
// Fetch all records
$sql = "SELECT * FROM events";
$eventsList = mysqli_query($con,$sql);
$response = array();
while($row = mysqli_fetch_assoc($eventsList)){
$response[] = array(
"eventid" => $row['id'],
"title" => $row['title'],
"description" => $row['description'],
"start" => $row['start_date'],
"end" => $row['end_date'],
);
}
echo json_encode($response);
exit;
6. Create a PHP File to Manage AJAX Requests
Create ajaxfile.php file to handle 4 requests:
1. Adding a New Event (addEvent)
- Reading the POST data and assigning it to variables.
- Check if the required variables have non-empty values.
- If all conditions are met, insert a new record into the
eventstable. - Capture the last inserted ID in the
$eventidvariable and set$statusto 1. - Construct a
$responsearray with keys such as ‘eventid’, ‘status’ and ‘message’. Assign the$eventidto the'eventid'key, set'status'to 1, and provide a success message. - In case the record isn’t inserted, set
$statusto 0 and include an error message. - Return the
$responsearray in JSON format.
2. Updating Event Dates (moveEvent)
- Retrieve the new dates from the POST request and update the relevant event’s ID.
- Ensure
$eventidis greater than 0 and that bothstart_dateandend_dateare not empty. - Check if the event ID exists in the
eventstable. If it does, update thestart_dateandend_datefields and set$statusto 1. - In the
$responsearray, set ‘status’ to 1 and provide a success message upon successful update. - If the update fails, set ‘status’ to 0 and include an error message.
- Return the
$responsearray in JSON format.
3. Editing Event Details (editEvent)
- Read the POST data containing event details and update the corresponding event ID.
- Ensure
$eventidis greater than 0 and that both ‘title’ and ‘description’ are not empty. - Check if the event ID exists in the
eventstable. If it does, update the ‘title’ and ‘description’ fields with the new POST values. - Set
$statusto 1 and construct the$responsearray with ‘status’ set to 1 and a success message. - If the update fails, set ‘status’ to 0 and include an error message.
- Return the
$responsearray in JSON format.
4. Deleting an Event (deleteEvent)
- Read the POST event ID and verify if it’s greater than 0.
- Check if the event ID exists in the
eventstable. If it does, delete the corresponding record and set$statusto 1. - In the
$responsearray, set ‘status’ to 1 and provide a success message upon successful deletion. - If deletion fails, set ‘status’ to 0 and include an error message.
- Return the
$responsearray in JSON format.
<?php
include "config.php";
$request = "";
// Read $_GET value
if(isset($_POST['request'])){
$request = $_POST['request'];
}
// Add New event
if($request == 'addEvent'){
// POST data
$title = ""; $description = "";
$start_date = ""; $end_date = "";
if(isset($_POST['title'])){
$title = $_POST['title'];
}
if(isset($_POST['description'])){
$description = $_POST['description'];
}
if(isset($_POST['start_date'])){
$start_date = $_POST['start_date'];
}
if(isset($_POST['start_date'])){
$end_date = $_POST['end_date'];
}
$response = array();
$status = 0;
if(!empty($title) && !empty($description) && !empty($start_date) && !empty($end_date) ){
// Insert record
$sql = "INSERT INTO events(title,description,start_date,end_date) VALUES('".$title."','".$description."','".$start_date."','".$end_date."')";
if(mysqli_query($con,$sql)){
$eventid = mysqli_insert_id($con);
$status = 1;
$response['eventid'] = $eventid;
$response['status'] = 1;
$response['message'] = 'Event created successfully.';
}
}
if($status == 0){
$response['status'] = 0;
$response['message'] = 'Event not created.';
}
echo json_encode($response);
exit;
}
// Move event
if($request == 'moveEvent'){
// POST data
$eventid = 0;
$start_date = ""; $end_date = "";
if(isset($_POST['eventid']) && is_numeric($_POST['eventid'])){
$eventid = $_POST['eventid'];
}
if(isset($_POST['start_date'])){
$start_date = $_POST['start_date'];
}
if(isset($_POST['end_date'])){
$end_date = $_POST['end_date'];
}
$response = array();
$status = 0;
// Check event id
$sql = "SELECT id FROM events WHERE id=".$eventid;
$result = mysqli_query($con,$sql);
if(mysqli_num_rows($result)){
// Update record
$sql = "UPDATE events SET start_date='".$start_date."',end_date='".$end_date."' WHERE id=".$eventid;
if(mysqli_query($con,$sql)){
$status = 1;
$response['status'] = 1;
$response['message'] = 'Event date updated successfully.';
}
}
if($status == 0){
$response['status'] = 0;
$response['message'] = 'Event date not updated.';
}
echo json_encode($response);
exit;
}
// Update event
if($request == 'editEvent'){
// POST data
$eventid = 0;
if(isset($_POST['eventid']) && is_numeric($_POST['eventid'])){
$eventid = $_POST['eventid'];
}
if(isset($_POST['title'])){
$title = $_POST['title'];
}
if(isset($_POST['description'])){
$description = $_POST['description'];
}
$response = array();
if($eventid > 0 && !empty($title) && !empty($description) ){
// Check event id
$sql = "SELECT id FROM events WHERE id=".$eventid;
$result = mysqli_query($con,$sql);
if(mysqli_num_rows($result)){
// Update record
$sql = "UPDATE events SET title='".$title."', description='".$description."' WHERE id=".$eventid;
if(mysqli_query($con,$sql)){
$status = 1;
$response['status'] = 1;
$response['message'] = 'Event updated successfully.';
}
}
}
if($status == 0){
$response['status'] = 0;
$response['message'] = 'Event not updated.';
}
echo json_encode($response);
exit;
}
// Delete Event
if($request == 'deleteEvent'){
// POST data
$eventid = 0;
if(isset($_POST['eventid']) && is_numeric($_POST['eventid'])){
$eventid = $_POST['eventid'];
}
$response = array();
$status = 0;
if($eventid > 0){
// Check event id
$sql = "SELECT id FROM events WHERE id=".$eventid;
$result = mysqli_query($con,$sql);
if(mysqli_num_rows($result)){
// Delete record
$sql = "DELETE FROM events WHERE id=".$eventid;
if(mysqli_query($con,$sql)){
$status = 1;
$response['status'] = 1;
$response['message'] = 'Event deleted successfully.';
}
}
}
if($status == 0){
$response['status'] = 0;
$response['message'] = 'Event not deleted.';
}
echo json_encode($response);
exit;
}
7. jQuery: Initializing FullCalendar and Managing Events
jQuery script to set up FullCalendar and manage events:
1. Initializing FullCalendar: Initiating FullCalendar within the <div id='calendar'> element. Specify several options for customization, such as:
- initialDate – By default, FullCalendar shows the current month but you can also specify a date in
yyyy-mm-ddformat. - height – By default, it fits on the whole screen but you change its height using this option. I set it to 600px.
- selectable – Allow event selection. The default value is
true. - editable – Allow event drag and drop on a different date. The default value is
false. - dayMaxEvents – It shows a more button if many events are set on a date. The default value is
false. - events – Load existing events. Specify the file from where need to load events. Set to
fetchevents.php. - select – This event trigger when click on a day. Using this to add a new event by displaying a popup box to enter event details.
2. Creating New Events: When a user selects a date, a pop-up alert will appear, allowing them to add an event. This alert includes input fields for the event title and description. Here’s the process:
- When the confirmation button is clicked, the entered details are captured.
- AJAX POST request is sent to
ajaxfile.phpwithrequest: 'addEvent'and event details. - Upon successful insertion
(response.status == 1), a new event is created usingcalendar.addEvent(). - Display a success or error message using the SweetAlert library.
3. Moving Events: When an event is dragged and dropped to a new date, the following steps occur:
- The event’s new start and end dates are captured.
- AJAX POST request is sent to
ajaxfile.phpwithrequest: 'moveEvent'and updated event details. - Interaction is synchronous
(async: false)to ensure proper handling.
4. Editing and Deleting Events: When an existing event is clicked, an alert appears with options to edit or delete the event. Here’s what happens:
- The alert displays the event’s title and description in the input fields.
- If ‘Update’ is confirmed, a new AJAX POST request is sent to
ajaxfile.phpwithrequest: 'editEvent'and updated event details. - Upon success
(response.status == 1), all events are refetched usingcalendar.refetchEvents(). - If ‘Delete’ is clicked, another AJAX POST request with
request: 'deleteEvent'and event ID is sent. - On success, the event is removed from the calendar using
arg.event.remove().
Make sure to insert this script at the end of the </body> section in your index.php file.
document.addEventListener('DOMContentLoaded', function() {
var calendarEl = document.getElementById('calendar');
var calendar = new FullCalendar.Calendar(calendarEl, {
initialDate: '<?= $currentData ?>',
height: '600px',
selectable: true,
editable: true,
dayMaxEvents: true, // allow "more" link when too many events
events: 'fetchevents.php', // Fetch all events
select: function(arg) { // Create Event
// Alert box to add event
Swal.fire({
title: 'Add New Event',
showCancelButton: true,
confirmButtonText: 'Create',
html:
'<input id="eventtitle" class="swal2-input" placeholder="Event name" style="width: 84%;" >' +
'<textarea id="eventdescription" class="swal2-input" placeholder="Event description" style="width: 84%; height: 100px;"></textarea>',
focusConfirm: false,
preConfirm: () => {
return [
document.getElementById('eventtitle').value,
document.getElementById('eventdescription').value
]
}
}).then((result) => {
if (result.isConfirmed) {
var title = result.value[0].trim();
var description = result.value[1].trim();
var start_date = arg.startStr;
var end_date = arg.endStr;
if(title != '' && description != ''){
// AJAX - Add event
$.ajax({
url: 'ajaxfile.php',
type: 'post',
data: {request: 'addEvent',title: title,description: description,start_date: start_date,end_date: end_date},
dataType: 'json',
success: function(response){
if(response.status == 1){
// Add event
calendar.addEvent({
eventid: response.eventid,
title: title,
description: description,
start: arg.start,
end: arg.end,
allDay: arg.allDay
})
// Alert message
Swal.fire(response.message,'','success');
}else{
// Alert message
Swal.fire(response.message,'','error');
}
}
});
}
}
})
calendar.unselect()
},
eventDrop: function (event, delta) { // Move event
// Event details
var eventid = event.event.extendedProps.eventid;
var newStart_date = event.event.startStr;
var newEnd_date = event.event.endStr;
// AJAX request
$.ajax({
url: 'ajaxfile.php',
type: 'post',
data: {request: 'moveEvent',eventid: eventid,start_date: newStart_date, end_date: newEnd_date},
dataType: 'json',
async: false,
success: function(response){
console.log(response);
}
});
},
eventClick: function(arg) { // Edit/Delete event
// Event details
var eventid = arg.event._def.extendedProps.eventid;
var description = arg.event._def.extendedProps.description;
var title = arg.event._def.title;
// Alert box to edit and delete event
Swal.fire({
title: 'Edit Event',
showDenyButton: true,
showCancelButton: true,
confirmButtonText: 'Update',
denyButtonText: 'Delete',
html:
'<input id="eventtitle" class="swal2-input" placeholder="Event name" style="width: 84%;" value="'+ title +'" >' +
'<textarea id="eventdescription" class="swal2-input" placeholder="Event description" style="width: 84%; height: 100px;">' + description + '</textarea>',
focusConfirm: false,
preConfirm: () => {
return [
document.getElementById('eventtitle').value,
document.getElementById('eventdescription').value
]
}
}).then((result) => {
if (result.isConfirmed) { // Update
var newTitle = result.value[0].trim();
var newDescription = result.value[1].trim();
if(newTitle != ''){
// AJAX - Edit event
$.ajax({
url: 'ajaxfile.php',
type: 'post',
data: {request: 'editEvent',eventid: eventid,title: newTitle, description: newDescription},
dataType: 'json',
async: false,
success: function(response){
if(response.status == 1){
// Refetch all events
calendar.refetchEvents();
// Alert message
Swal.fire(response.message, '', 'success');
}else{
// Alert message
Swal.fire(response.message, '', 'error');
}
}
});
}
} else if (result.isDenied) { // Delete
// AJAX - Delete Event
$.ajax({
url: 'ajaxfile.php',
type: 'post',
data: {request: 'deleteEvent',eventid: eventid},
dataType: 'json',
async: false,
success: function(response){
if(response.status == 1){
arg.event.remove();
// Alert message
Swal.fire(response.message, '', 'success');
}else{
// Alert message
Swal.fire(response.message, '', 'error');
}
}
});
}
})
}
});
calendar.render();
});
8. Demo
9. Conclusion
In the example, I have used sweetAlert plugin to display a popup box to enter and display details. You can also use any other plugin with which you are compatible.
You have to create a separate file to fetch all events. It is not possible to manage it from a single file to handle all Fullcalendar actions – update/delete. It will cause problems during refetch events after editing an event.
If you found this tutorial helpful then don't forget to share.