Event CRUD in FullCalendar using PHP and jQuery Tutorial

In this tutorial, I will show how to Create CRUD Operation in FullCalendar with PHP and MySQL Database. here, you will learn to add, edit, view, and delete events on the calendar. The events will be stored in MySQL Database. This feature can be useful for your future PHP Projects such as Appointment Management Systems or Scheduling Systems.

We will create a simple PHP application with dummy MySQL data that stores the events/schedules. The main goal of the application is to have features or functionalities that adds, updates, view, and delete events in FullCalendar.

Getting Started

Download XAMPP as your local web server to run our PHP Script. After Installing the virtual server, open the XAMPP's Control Panel and start the Apache Server and MySQL.

Since, that events will be shown in a calendar view using the FullCalendar library/plugin, download the library's resources here.

Download Bootstrap v5 and jQuery for the interface design of the application that we'll be creating. After that move the library directory to the folder where you will store the source code on your end.

Creating The Database

Open a new tab in your browser and browse the XAMPP's PHPMyAdmin. Next, create a new database naming dummy_db. Then, navigate the page into the SQL Tab/Page and paste SQL Script below to the provided text field. Lastly, click the Go Button to execute the script.

  1. CREATE TABLE `schedule_list` (
  2.   `title` text NOT NULL,
  3.   `description` text NOT NULL,
  4.   `start_datetime` datetime NOT NULL,
  5.   `end_datetime` datetime DEFAULT NULL

Creating The Database Connection

Open a text editor such as sublime text, notepad++, or VSCode. Create a new PHP File naming db-connect.php. Then, Copy/Paste the code below. Make sure to configure the database credentials according to your setup.

  1. <?php
  2. $host     = 'localhost';
  3. $username = 'root';
  4. $password = '';
  5. $dbname   ='dummy_db';
  6.  
  7. $conn = new mysqli($host, $username, $password, $dbname);
  8. if(!$conn){
  9.     die("Cannot connect to the database.". $conn->error);
  10. }

Or, you can also import the SQL File I provided along with working source code file. The file is known as dummy_db.sql and located inside the db folder.

Creating The Interface

Next, we will be creating the interface of our page. The page contains the HTML elements and PHP script that shows the data and query data in the database. Copy/Paste the script below and save it as index.php.

  1. <?php require_once('db-connect.php') ?>
  2. <!DOCTYPE html>
  3. <html lang="en">
  4.  
  5.     <meta charset="UTF-8">
  6.     <meta http-equiv="X-UA-Compatible" content="IE=edge">
  7.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  8.     <title>Scheduling</title>
  9.     <link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css" integrity="sha384-AYmEC3Yw5cVb3ZcuHtOA93w35dYTsvhLPVnYs9eStHfGJvOvKxVfELGroGkvsg+p" crossorigin="anonymous"/>
  10.     <link rel="stylesheet" href="./css/bootstrap.min.css">
  11.     <link rel="stylesheet" href="./fullcalendar/lib/main.min.css">
  12.     <script src="./js/jquery-3.6.0.min.js"></script>
  13.     <script src="./js/bootstrap.min.js"></script>
  14.     <script src="./fullcalendar/lib/main.min.js"></script>
  15.     <style>
  16.         :root {
  17.             --bs-success-rgb: 71, 222, 152 !important;
  18.         }
  19.  
  20.         html,
  21.         body {
  22.             height: 100%;
  23.             width: 100%;
  24.             font-family: Apple Chancery, cursive;
  25.         }
  26.  
  27.         .btn-info.text-light:hover,
  28.         .btn-info.text-light:focus {
  29.             background: #000;
  30.         }
  31.         table, tbody, td, tfoot, th, thead, tr {
  32.             border-color: #ededed !important;
  33.             border-style: solid;
  34.             border-width: 1px !important;
  35.         }
  36.     </style>
  37. </head>
  38.  
  39. <body class="bg-light">
  40.     <nav class="navbar navbar-expand-lg navbar-dark bg-dark bg-gradient" id="topNavBar">
  41.         <div class="container">
  42.             <a class="navbar-brand" href="https://sourcecodester.com">
  43.             Sourcecodester
  44.             </a>
  45.  
  46.             <div>
  47.                 <b class="text-light">Sample Scheduling</b>
  48.             </div>
  49.         </div>
  50.     </nav>
  51.     <div class="container py-5" id="page-container">
  52.         <div class="row">
  53.             <div class="col-md-9">
  54.                 <div id="calendar"></div>
  55.             </div>
  56.             <div class="col-md-3">
  57.                 <div class="cardt rounded-0 shadow">
  58.                     <div class="card-header bg-gradient bg-primary text-light">
  59.                         <h5 class="card-title">Schedule Form</h5>
  60.                     </div>
  61.                     <div class="card-body">
  62.                         <div class="container-fluid">
  63.                             <form action="save_schedule.php" method="post" id="schedule-form">
  64.                                 <input type="hidden" name="id" value="">
  65.                                 <div class="form-group mb-2">
  66.                                     <label for="title" class="control-label">Title</label>
  67.                                     <input type="text" class="form-control form-control-sm rounded-0" name="title" id="title" required>
  68.                                 </div>
  69.                                 <div class="form-group mb-2">
  70.                                     <label for="description" class="control-label">Description</label>
  71.                                     <textarea rows="3" class="form-control form-control-sm rounded-0" name="description" id="description" required></textarea>
  72.                                 </div>
  73.                                 <div class="form-group mb-2">
  74.                                     <label for="start_datetime" class="control-label">Start</label>
  75.                                     <input type="datetime-local" class="form-control form-control-sm rounded-0" name="start_datetime" id="start_datetime" required>
  76.                                 </div>
  77.                                 <div class="form-group mb-2">
  78.                                     <label for="end_datetime" class="control-label">End</label>
  79.                                     <input type="datetime-local" class="form-control form-control-sm rounded-0" name="end_datetime" id="end_datetime" required>
  80.                                 </div>
  81.                             </form>
  82.                         </div>
  83.                     </div>
  84.                     <div class="card-footer">
  85.                         <div class="text-center">
  86.                             <button class="btn btn-primary btn-sm rounded-0" type="submit" form="schedule-form"><i class="fa fa-save"></i> Save</button>
  87.                             <button class="btn btn-default border btn-sm rounded-0" type="reset" form="schedule-form"><i class="fa fa-reset"></i> Cancel</button>
  88.                         </div>
  89.                     </div>
  90.                 </div>
  91.             </div>
  92.         </div>
  93.     </div>
  94.     <!-- Event Details Modal -->
  95.     <div class="modal fade" tabindex="-1" data-bs-backdrop="static" id="event-details-modal">
  96.         <div class="modal-dialog modal-dialog-centered">
  97.             <div class="modal-content rounded-0">
  98.                 <div class="modal-header rounded-0">
  99.                     <h5 class="modal-title">Schedule Details</h5>
  100.                     <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
  101.                 </div>
  102.                 <div class="modal-body rounded-0">
  103.                     <div class="container-fluid">
  104.                         <dl>
  105.                             <dt class="text-muted">Title</dt>
  106.                             <dd id="title" class="fw-bold fs-4"></dd>
  107.                             <dt class="text-muted">Description</dt>
  108.                             <dd id="description" class=""></dd>
  109.                             <dt class="text-muted">Start</dt>
  110.                             <dd id="start" class=""></dd>
  111.                             <dt class="text-muted">End</dt>
  112.                             <dd id="end" class=""></dd>
  113.                         </dl>
  114.                     </div>
  115.                 </div>
  116.                 <div class="modal-footer rounded-0">
  117.                     <div class="text-end">
  118.                         <button type="button" class="btn btn-primary btn-sm rounded-0" id="edit" data-id="">Edit</button>
  119.                         <button type="button" class="btn btn-danger btn-sm rounded-0" id="delete" data-id="">Delete</button>
  120.                         <button type="button" class="btn btn-secondary btn-sm rounded-0" data-bs-dismiss="modal">Close</button>
  121.                     </div>
  122.                 </div>
  123.             </div>
  124.         </div>
  125.     </div>
  126.     <!-- Event Details Modal -->
  127.  
  128. <?php
  129. $schedules = $conn->query("SELECT * FROM `schedule_list`");
  130. $sched_res = [];
  131. foreach($schedules->fetch_all(MYSQLI_ASSOC) as $row){
  132.     $row['sdate'] = date("F d, Y h:i A",strtotime($row['start_datetime']));
  133.     $row['edate'] = date("F d, Y h:i A",strtotime($row['end_datetime']));
  134.     $sched_res[$row['id']] = $row;
  135. }
  136. ?>
  137. <?php
  138. if(isset($conn)) $conn->close();
  139. ?>
  140. </body>
  141.     var scheds = $.parseJSON('<?= json_encode($sched_res) ?>')
  142. <script src="./js/script.js"></script>
  143.  
  144. </html>

Creating The PHP Queries

Next, we will be creating the PHP queries. The codes below is the scripts that inserts, updates, and delete event data in our database. Save the files according to the filename above each scripts.

save_schedule.php

This scripts contains the insert and update query scripts.

  1. <?php
  2. require_once('db-connect.php');
  3. if($_SERVER['REQUEST_METHOD'] !='POST'){
  4.     echo "<script> alert('Error: No data to save.'); location.replace('./') </script>";
  5.     $conn->close();
  6.     exit;
  7. }
  8. extract($_POST);
  9. $allday = isset($allday);
  10.  
  11. if(empty($id)){
  12.     $sql = "INSERT INTO `schedule_list` (`title`,`description`,`start_datetime`,`end_datetime`) VALUES ('$title','$description','$start_datetime','$end_datetime')";
  13. }else{
  14.     $sql = "UPDATE `schedule_list` set `title` = '{$title}', `description` = '{$description}', `start_datetime` = '{$start_datetime}', `end_datetime` = '{$end_datetime}' where `id` = '{$id}'";
  15. }
  16. $save = $conn->query($sql);
  17. if($save){
  18.     echo "<script> alert('Schedule Successfully Saved.'); location.replace('./') </script>";
  19. }else{
  20.     echo "<pre>";
  21.     echo "An Error occurred.<br>";
  22.     echo "Error: ".$conn->error."<br>";
  23.     echo "SQL: ".$sql."<br>";
  24.     echo "</pre>";
  25. }
  26. $conn->close();
  27. ?>
delete_schedule.php

This scripts contains the delete query scripts.

  1.         <?php
  2.         require_once('db-connect.php');
  3.         if(!isset($_GET['id'])){
  4.             echo "<script> alert('Undefined Schedule ID.'); location.replace('./') </script>";
  5.             $conn->close();
  6.             exit;
  7.         }
  8.  
  9.         $delete = $conn->query("DELETE FROM `schedule_list` where id = '{$_GET['id']}'");
  10.         if($delete){
  11.             echo "<script> alert('Event has deleted successfully.'); location.replace('./') </script>";
  12.         }else{
  13.             echo "<pre>";
  14.             echo "An Error occured.<br>";
  15.             echo "Error: ".$conn->error."<br>";
  16.             echo "SQL: ".$sql."<br>";
  17.             echo "</pre>";
  18.         }
  19.         $conn->close();
  20.  
  21.         ?>

Creating The Main Function

Lastly, the script below is a javascript codes that initialize the calendar, render the events in the calendar, and some other functionalities. Save this file as script.js.

  1.     var calendar;
  2.     var Calendar = FullCalendar.Calendar;
  3.     var events = [];
  4.     $(function() {
  5.         if (!!scheds) {
  6.             Object.keys(scheds).map(k => {
  7.                 var row = scheds[k]
  8.                 events.push({ id: row.id, title: row.title, start: row.start_datetime, end: row.end_datetime });
  9.             })
  10.         }
  11.         var date = new Date()
  12.         var d = date.getDate(),
  13.             m = date.getMonth(),
  14.             y = date.getFullYear()
  15.  
  16.         calendar = new Calendar(document.getElementById('calendar'), {
  17.             headerToolbar: {
  18.                 left: 'prev,next today',
  19.                 right: 'dayGridMonth,dayGridWeek,list',
  20.                 center: 'title',
  21.             },
  22.             selectable: true,
  23.             themeSystem: 'bootstrap',
  24.             //Random default events
  25.             events: events,
  26.             eventClick: function(info) {
  27.                 var _details = $('#event-details-modal')
  28.                 var id = info.event.id
  29.                 if (!!scheds[id]) {
  30.                     _details.find('#title').text(scheds[id].title)
  31.                     _details.find('#description').text(scheds[id].description)
  32.                     _details.find('#start').text(scheds[id].sdate)
  33.                     _details.find('#end').text(scheds[id].edate)
  34.                     _details.find('#edit,#delete').attr('data-id', id)
  35.                     _details.modal('show')
  36.                 } else {
  37.                     alert("Event is undefined");
  38.                 }
  39.             },
  40.             eventDidMount: function(info) {
  41.                 // Do Something after events mounted
  42.             },
  43.             editable: true
  44.         });
  45.  
  46.         calendar.render();
  47.  
  48.         // Form reset listener
  49.         $('#schedule-form').on('reset', function() {
  50.             $(this).find('input:hidden').val('')
  51.             $(this).find('input:visible').first().focus()
  52.         })
  53.  
  54.         // Edit Button
  55.         $('#edit').click(function() {
  56.             var id = $(this).attr('data-id')
  57.             if (!!scheds[id]) {
  58.                 var _form = $('#schedule-form')
  59.                 console.log(String(scheds[id].start_datetime), String(scheds[id].start_datetime).replace(" ", "\\t"))
  60.                 _form.find('[name="id"]').val(id)
  61.                 _form.find('[name="title"]').val(scheds[id].title)
  62.                 _form.find('[name="description"]').val(scheds[id].description)
  63.                 _form.find('[name="start_datetime"]').val(String(scheds[id].start_datetime).replace(" ", "T"))
  64.                 _form.find('[name="end_datetime"]').val(String(scheds[id].end_datetime).replace(" ", "T"))
  65.                 $('#event-details-modal').modal('hide')
  66.                 _form.find('[name="title"]').focus()
  67.             } else {
  68.                 alert("Event is undefined");
  69.             }
  70.         })
  71.  
  72.         // Delete Button / Deleting an Event
  73.         $('#delete').click(function() {
  74.             var id = $(this).attr('data-id')
  75.             if (!!scheds[id]) {
  76.                 var _conf = confirm("Are you sure to delete this scheduled event?");
  77.                 if (_conf === true) {
  78.                     location.href = "./delete_schedule.php?id=" + id;
  79.                 }
  80.             } else {
  81.                 alert("Event is undefined");
  82.             }
  83.         })
  84.     })

DEMO VIDEO

That's it! You can now test the application in your browser and see if we have met our goal on this tutorial. If ever you have encountered any errors, please review your source code by differentiating it from the source code I provided above. You can also test the working source code I created for this tutorial. You can download the source code zip file below this article.

That is the end of this tutorial. I hope you'll find this Tutorial useful for your current and future PHP projects.

Happy Coding :)

Comments

Thanks for this tutorial! I found this bug: dragging and dropping an event works in the UI, but start and end date aren't updated in database. Could you please fix this?

Thanks for this tutorial! Can we get this example with MVC

In reply to by Hatim Abr (not verified)

you got it?

how to change that dot color?

Wonderful I have been looking such a code for a long time

How do I change the calendar language? I want to leave it in Portuguese - Brazil, another function: because the time is in PM, for example, I schedule a service at 6:00 pm, but 6:00 PM appears in the event view. Can someone help me?

In reply to by Nathan Moreira (not verified)

I have the same problem but in italian, did you figure it out how to change the language?

In reply to by piSelloBeLLo (not verified)

currently having the same problem did you find it?

wish i had found this 2 days ago.... why this isnt on their docs i'll never know. they should be paying you !

How i change the language of months to italian?

Add new comment