INTRODUCTION
ONLINE RESERVATION
Welcome to a tutorial on how to create a simple PHP Reservation system. So you are offering a service and want to open up for online reservations? Or maybe you want to open up a room available for renting online? Fear not – This guide will walk you through the steps on how to create a reservation system. Read on!
I have included a zip file with all the source code at the end of this tutorial, so you don’t have to copy-paste everything… Or if you just want to dive straight in.
CONFESSION
AN HONEST DISCLOSURE
Quick, hide your wallets! I am an affiliate partner of Google, eBay, Adobe, Bluehost, Clickbank, and more. There are affiliate links and advertisements throughout this website. Whenever you buy things from the evil links that I recommend, I will make a commission. Nah. These are just things to keep the blog going, and allows me to give more good stuff to you guys - for free. So thank you if you decide to pick up my recommendations!
NAVIGATION
TABLE OF CONTENTS
Prelude | Step 1 | Step 2 |
Step 3 | Extra | Closing |
PRELUDE
OVERVIEW & ASSUMPTIONS
Before we go into the actual scripts, here is a small section on the overview of the simple system, and some assumptions that I have made – So you know what to expect from this guide.
ASSUMPTIONS
Some of you guys here probably have an existing project or website, and planning to build a reservation system on top of it. So we are not going to reinvent the wheel and create yet another admin panel on top of your existing one. This will be a pure “reservation only”, and for those of you who are starting fresh, I will leave the link on how to create an admin panel at the end of this guide.
Also, I shall assume that most of you guys are already established code ninjas. Comfortable with PHP, Javascript, AJAX, CSS, and SQL – I will not go into “boring mode” to explain every tiny detail such as “how does AJAX work”.
OVERVIEW
There are 3 parts to this project:
- The database – We need somewhere to store the reservations now, do we?
- Server-side scripts – Processes the user requests, stores reservation into the database, send email, etc…
- Client-side scripts – The reservation page itself.
Now, everyone here probably has different reservation requirements – Whole day reservation, date range reservation, time slot reservation, the next day only, within 30 days only, etc… It is impossible to deal with all the possible cases, so this guide will only cover 3 of the most common scenarios –
- Whole day reservation
- Time slot reservation
- Date range reservation
You will have to implement the specific rules and restrictions yourself.
PROJECT FOLDERS
If you have downloaded the zip file at the end of this guide, there will be 2 folders in this project. If you are following up step-by-step, just create the following folders to better organize things.
- lib – Contains all the PHP library files.
- public – Where we put all the public client-side CSS, Javascript, and images.
Optionally, you can create a .htaccess
file in the lib
folder to stop public access to the library files… Actually, I will highly recommend doing this to protect your system.
Deny from all
STEP 1
THE DATABASE
Now that we have all the basics out of the way, let us start by building the foundation of the project – By creating a database reservation table.
RESERVATION TABLE
CREATE TABLE `reservations` (
`res_id` int(11) NOT NULL,
`res_name` varchar(255) NOT NULL,
`res_email` varchar(255) NOT NULL,
`res_tel` varchar(60) NOT NULL,
`res_notes` text,
`res_date` date DEFAULT NULL,
`res_slot` varchar(4) DEFAULT NULL,
`res_start` date DEFAULT NULL,
`res_end` date DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `reservations`
ADD PRIMARY KEY (`res_id`),
ADD KEY `res_name` (`res_name`),
ADD KEY `res_email` (`res_email`),
ADD KEY `res_tel` (`res_tel`),
ADD KEY `res_date` (`res_date`),
ADD KEY `res_slot` (`res_slot`),
ADD KEY `res_start` (`res_start`),
ADD KEY `res_end` (`res_end`);
ALTER TABLE `reservations`
MODIFY `res_id` int(11) NOT NULL AUTO_INCREMENT;
COMMIT;
THE FIELDS
Field | Description |
res_id | Reservation ID. Primary key, auto-increment. |
res_name | Name of the customer. |
res_email | Email of the customer. |
res_tel | The telephone number of customer. |
res_note | Reservation notes, if any. |
res_date | For whole day or time slot booking – You can remove this field if you are doing date range booking. |
res_slot | For time slot booking – You can remove this field if you are doing whole day or date range booking. |
res_start | Start date for date range – You can remove this field if you are doing a single day booking. |
res_end | End date for date range – You can remove this field if you are doing a single day booking. |
STEP 2
SERVER-SIDE SCRIPT
Now that we are done with the database foundation, let us create the server-side scripts that will process the reservations.
CONFIG FILE
First, let us create a config file to safely keep all the database and settings in – Do remember to change the database settings to your own.
DATABASE LIBRARY
pdo = new PDO(
$str, DB_USER, DB_PASSWORD, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false
]
);
return true;
}
// ERROR - DO SOMETHING HERE
// THROW ERROR MESSAGE OR SOMETHING
catch (Exception $ex) {
print_r($ex);
die();
}
}
function __destruct() {
// __destruct() : close connection when done
if ($this->stmt !== null) { $this->stmt = null; }
if ($this->pdo !== null) { $this->pdo = null; }
}
function exec($sql, $data=null) {
// exec() : run insert, replace, update, delete query
// PARAM $sql : SQL query
// $data : array of data
try {
$this->stmt = $this->pdo->prepare($sql);
$this->stmt->execute($data);
$this->lastID = $this->pdo->lastInsertId();
} catch (Exception $ex) {
$this->error = $ex;
return false;
}
$this->stmt = null;
return true;
}
function fetch($sql, $cond=null, $key=null, $value=null) {
// fetch() : perform select query
// PARAM $sql : SQL query
// $cond : array of conditions
// $key : sort in this $key=>data order, optional
// $value : $key must be provided, sort in $key=>$value order
$result = false;
try {
$this->stmt = $this->pdo->prepare($sql);
$this->stmt->execute($cond);
if (isset($key)) {
$result = array();
if (isset($value)) {
while ($row = $this->stmt->fetch(PDO::FETCH_NAMED)) {
$result[$row[$key]] = $row[$value];
}
} else {
while ($row = $this->stmt->fetch(PDO::FETCH_NAMED)) {
$result[$row[$key]] = $row;
}
}
} else {
$result = $this->stmt->fetchAll();
}
} catch (Exception $ex) {
$this->error = $ex;
return false;
}
$this->stmt = null;
return $result;
}
}
?>
Now, this is a “lazy database library” script that I have been using for almost every one of my projects and tutorials – This is nothing much, but a pretty simple PDO database script that you can reuse in your own projects if you want.
Function | Description |
__construct | The constructor, automatically connects to the database when the object is being created. |
__destruct | The destructor, automatically closes the database when the object is being destroyed. |
exec | Run an insert, replace, update, or delete query. |
fetch | Run a select query |
RESERVATION LIBRARY
fetch($sql, $cond);
if (count($check)>0) {
$this->error = $email . " has already reserved " . $date;
return false;
}
// Process reservation
$sql = "INSERT INTO `reservations` (`res_name`, `res_email`, `res_tel`, `res_notes`, `res_date`) VALUES (?,?,?,?,?)";
$cond = [$name, $email, $tel, $notes, $date];
return $this->exec($sql, $cond);
}
/* [TIME SLOT BOOKING] */
function bookSlot ($name, $email, $tel, $date, $slot, $notes="") {
// bookSlot() : reserve for the time slot
// Check if customer already booked on the time slot
$sql = "SELECT * FROM `reservations` WHERE `res_email`=? AND `res_date`=? AND `res_slot`=?";
$cond = [$email, $date, $slot];
$check = $this->fetch($sql, $cond);
if (count($check)>0) {
$this->error = $email . " has already reserved " . $date . " " . $slot;
return false;
}
// Process reservation
$sql = "INSERT INTO `reservations` (`res_name`, `res_email`, `res_tel`, `res_notes`, `res_date`, `res_slot`) VALUES (?,?,?,?,?,?)";
$cond = [$name, $email, $tel, $notes, $date, $slot];
return $this->exec($sql, $cond);
}
/* [DATE RANGE BOOKING] */
function bookRange ($name, $email, $tel, $start, $end, $notes="") {
// bookRange() : reserve for the date range
// Check if customer already booked within the date range
$sql = "SELECT * FROM `reservations` WHERE (`res_start` BETWEEN ? AND ?) OR (`res_end` BETWEEN ? AND ?)";
$cond = [$start, $end, $start, $end];
$check = $this->fetch($sql, $cond);
if (count($check)>0) {
$this->error = $email . " has already reserved between " . $start . " and " . $end;
return false;
}
// Process reservation
$sql = "INSERT INTO `reservations` (`res_name`, `res_email`, `res_tel`, `res_notes`, `res_start`, `res_end`) VALUES (?,?,?,?,?,?)";
$cond = [$name, $email, $tel, $notes, $start, $end];
return $this->exec($sql, $cond);
}
/* [GET RESERVATION] */
// @TODO - There are 101 ways to get/search for the reservations
// This is a simple example that will get all reservations within a selected date range
// Please do build your own functions in this library!
function bookGet ($start, $end) {
// bookGet() : get reservation for selected month/year
$search = $this->fetch(
"SELECT * FROM `reservations` WHERE `res_date` BETWEEN ? AND ?",
[$start, $end]
);
return count($search)==0 ? false : $search ;
}
}
Next, we build a library to store and retrieve the reservations from the database. This is sort of a barebones one, and you might want to further beef it up with your own functions.
Function | Description |
bookDay | Use this if you are doing single full-day booking, remove it if not using. |
bookSlot | Use this if you are doing single day, time slot booking, remove it if not using. |
bookRange | Use this if you are doing date range booking, remove it if not using. |
bookGet | A sample function on how you can use the database library to retrieve the reservations. |
AJAX HANDLER
session_start();
if (!is_array($_SESSION['user'])) {
die(json_encode([
"status" => 0,
"message" => "You must be signed in first"
]));
}
*/
// HANDLE AJAX REQUEST
if ($_POST['req']) { switch ($_POST['req']) {
// INVALID REQUEST
default :
echo json_encode([
"status" => 0,
"message" => "Invalid request"
]);
break;
// SHOW CALENDAR OR DATE SELECTOR
case "show-cal":
// Selected month and year + Various date yoga
// * Will take current server time if not provided
$thisMonth = (is_numeric($_POST['month']) && $_POST['month']>=1 && $_POST['month']1];
}}
// Days that have already past are not selectable
// Earliest selectable is next day - Change this if you want
$inow = 1;
if ($thisYear==$yearNow && $thisMonth==$monthNow) {
for ($inow=1; $inow$inow, "b"=>1];
}
}
// Populate the rest of the selectable days
for ($inow; $inow$inow];
}
/* This is an alternate version to show how you can put in date restrictions
* For example, close off Sat & Sun reservations
$dayNow = date("N", strtotime(sprintf("%s-%02u-%02u", $thisYear, $thisMonth, $inow)));
for ($inow; $inow$inow, "b"=>1]; }
else { $squares[] = ["d"=>$inow]; }
$dayNow++;
if ($dayNow==8) { $dayNow = 1; }
}
*/
// If the last day of the month is not Saturday, pad with blanks
if ($endDay != 6) {
$blanks = $endDay==7 ? 6 : 6-$endDay;
for ($i=0; $i1];
}
}
// Draw calendar - Limit your selectable periods here if you want
// Month selector
$months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
echo "";
// Year selector
echo "";
// Dates
echo "";
// First row - Days of week
$days = ["Sun", "Mon", "Tue", "Wed", "Thur", "Fri", "Sat"];
foreach ($days as $d) { echo "$d "; }
echo " ";
// Following rows - Days in month
$total = count($squares);
$first = true;
for ($i=0; $i";
if ($squares[$i]['d']) { echo $squares[$i]['d']; }
echo "";
if ($i!=0 && ($i+1)%7==0) {
echo " ";
}
}
echo "
";
break;
// SHOW TIME SLOT SELECTOR
case "show-slot":
/* Do your own time slot logic here - AM/PM, Hourly, Bi-hourly, etc...
// You can use the $_POST['date'] variable to restrict
// E.g. AM slots for Sat & Sun
$selected = date("N", strtotime($_POST['date']));
$weekend = $selected==6 || $selected==7;
if ($weekend) { RESTRICT }
else { AS USUAL } */ ?>
bookDay(
$_POST['name'], $_POST['email'], $_POST['tel'], $_POST['date'],
$_POST['notes'] ? $_POST['notes'] : ""
);
/* You can send an email if you want
if ($pass) {
$message = "";
foreach ($_POST as $k=>$v) {
$message .= $k . " - " . $v;
}
@mail("[email protected]", "Reservation receieved", $message);
}
*/
// Server response
echo json_encode([
"status" => $pass ? 1 : 0,
"message" => $pass ? "OK" : $reslib->error
]);
break;
// ADD NEW RESERVATION - TIME SLOT BOOKING
case "book-slot":
// Save reservation to database
$pass = $reslib->bookSlot(
$_POST['name'], $_POST['email'], $_POST['tel'], $_POST['date'], $_POST['slot'],
$_POST['notes'] ? $_POST['notes'] : ""
);
/* You can send an email if you want
if ($pass) {
$message = "";
foreach ($_POST as $k=>$v) {
$message .= $k . " - " . $v;
}
@mail("[email protected]", "Reservation receieved", $message);
}
*/
// Server response
echo json_encode([
"status" => $pass ? 1 : 0,
"message" => $pass ? "OK" : $reslib->error
]);
break;
// ADD NEW RESERVATION - DATE RANGE BOOKING
case "book-range":
// Save reservation to database
$pass = $reslib->bookRange(
$_POST['name'], $_POST['email'], $_POST['tel'], $_POST['start'], $_POST['end'],
$_POST['notes'] ? $_POST['notes'] : ""
);
/* You can send an email if you want
if ($pass) {
$message = "";
foreach ($_POST as $k=>$v) {
$message .= $k . " - " . $v;
}
@mail("[email protected]", "Reservation receieved", $message);
}
*/
// Server response
echo json_encode([
"status" => $pass ? 1 : 0,
"message" => $pass ? "OK" : $reslib->error
]);
break;
}}
Finally, the library files will not do anything on their own, thus the reason for this seemingly crazy AJAX handler script… It looks confusing, but it’s really nothing too much.
WHAT THE AJAX!?
How the AJAX handler works is pretty straightforward. You send a $_POST['req']
request to this script (on what you want to process), followed by the required parameters.
Request | Description |
show-cal | Generates an HTML date picker for the given period $_POST['month'] and $_POST['year'] . Used by all 3 different booking scenarios, will go into further details below. |
show-slot | Shows the available time slots for the given date $_POST['date'] , this is just a simple AM/PM selector. Modify this to fit your own project – Could be different hourly slots, or maybe even bi-hourly. |
book-day | Used for full-day booking, you can remove this if you are offering timeslot or date range booking. Requires name, email, telephone, date, and an optional note. |
book-slot | Used for timeslot booking, you can remove this if you are offering full-day or date range booking. Requires name, email, telephone, date, timeslot, and an optional note. |
book-range | Used for date range booking, you can remove this if you are offering full-day or timeslot booking. Requires name, email, telephone, start date, end date, and an optional note. |
So there you go – You can actually remove a few sections of this script that you don’t use, and it really isn’t as complicated.
THE CRAZY CALENDAR
The craziest part of the server-side script is probably generating the selectable dates for a given month. We will walk through this section of the AJAX handler here in more details.
- The top part of the script does a lot of days, month, and year calculations – The first day of the selected period, last day, days in the month, etc…
- Do take note that if you don’t pass a
$_POST['month']
and$_POST['year']
, the script will take the current time on the server by default. - Before the generation of the HTML calendar, we will use an array
$squares
to contain all the render information. - For a start, there are many months that don’t start on a Sunday. For example, the first day of the month may be a Tuesday, and we will need to pad the calendar with blank squares from Sunday to Monday – To do that we simply use
$squares[] = ["b"=>1]
twice to pad the empty squares. - Following up are the days within the month itself – Which we pad with
$squares[] = ["d"=>DAY]
. - If you want to block certain days from being available, you can use
$squares[] = ["d"=>DAY, "b"=>1]
. - Then, many months don’t end on a Saturday, so we do the same empty padding trick.
- Finally, we will render the HTML calendar from the information contained in
$squares
.
STEP 3
CLIENT-SIDE SCRIPT
Finally, we only need to complete the puzzle by building the actual HTML reservation page itself – We will run through each of the 3 different scenarios here… Feel free to skip the ones that do not apply to you.
FULL DAY BOOKING
THE HTML
PHP Reservation Demo - Single Full Day Booking
RESERVATION
This should be pretty easy to understand – Just a normal HTML reservation form. But take note of two things here –
- We will load the date selector into via AJAX. This way, we can do all the proper restrictions and checks on the server-side first.
- The submit button is disabled. We will only enable this when the date picker is fully loaded.
THE JAVASCRIPT
public/reserve-day.jsvar res = { cal : function () { // res.cal() : show calendar // Disable submit first document.getElementById("res_go").disabled = true; // AJAX data var data = new FormData(); data.append('req', 'show-cal'); // Get selected month & year - If they exist var select = document.querySelector("#res_date select.month"); if (select!=null) { data.append('month', select.value); select = document.querySelector("#res_date select.year"); data.append('year', select.value); } // AJAX call var xhr = new XMLHttpRequest(); xhr.open('POST', "ajax-reserve.php", true); xhr.onload = function(){ // Set contents, click, change actions document.getElementById("res_date").innerHTML = this.response; select = document.querySelector("#res_date select.month"); select.addEventListener("change", res.cal); select = document.querySelector("#res_date select.year"); select.addEventListener("change", res.cal); select = document.querySelectorAll("#res_date .pick, #res_date .active"); for (var i of select) { i.addEventListener("click", res.pick); } // Enable submit document.getElementById("res_go").disabled = false; }; xhr.send(data); }, pick : function () { // res.pick() : change selected date var select = document.querySelector("#res_date .active"); if (select!=this) { select.classList.remove("active"); select.classList.add("pick"); this.classList.remove("pick"); this.classList.add("active"); } }, save : function () { // res.save() : save the reservation // Selected date var select = document.querySelector("#res_date td.active").innerHTML; if (select.length==1) { select = "0" + select; } select = document.querySelector("#res_date select.month").value + "-" + select; select = document.querySelector("#res_date select.year").value + "-" + select; // AJAX data var data = new FormData(); data.append('req', 'book-day'); data.append('name', document.getElementById("res_name").value); data.append('email', document.getElementById("res_email").value); data.append('tel', document.getElementById("res_tel").value); data.append('notes', document.getElementById("res_notes").value); data.append('date', select); // AJAX call var xhr = new XMLHttpRequest(); xhr.open('POST', "ajax-reserve.php", true); xhr.onload = function(){ var res = JSON.parse(this.response); // OK - Redirect to thank you page if (res.status==1) { location.href = "thank-you.html"; } // ERROR - show error else { alert(res.message); } }; xhr.send(data); return false; } }; window.addEventListener("load", res.cal);
This Javascript looks complicated, but actually, there are only 3 functions here.
Function Description res.cal() This function will fire up when the page is initially loaded, and whenever the user changes the month/year. It will do an AJAX call to the server to render the calendar for the selected period. res.pick() This function is fired when the user clicks on a date on the calendar. It simply updates the HTML to the newly selected date. res.save() Submit and process the reservation form. TIMESLOT BOOKING
THE HTML
reserve-slot.htmlPHP Reservation Demo - Single Time Slot Booking RESERVATION
The HTML of the timeslot reservation is pretty much the same as full-day – This is but a simple HTML form, but take note:
- The date selector will be loaded into via AJAX.
- Following up, the timeslot selector will be loaded into
via AJAX.- The submit button is disabled. We will only enable this when both the date picker and timeslot picker are fully loaded.
THE JAVASCRIPT
public/reserve-slot.jsvar res = { cal : function () { // res.cal() : show calendar // Disable submit first document.getElementById("res_go").disabled = true; // AJAX data var data = new FormData(); data.append('req', 'show-cal'); // Get selected month & year - If they exist var select = document.querySelector("#res_date select.month"); if (select!=null) { data.append('month', select.value); select = document.querySelector("#res_date select.year"); data.append('year', select.value); } // AJAX call var xhr = new XMLHttpRequest(); xhr.open('POST', "ajax-reserve.php", true); xhr.onload = function(){ // Set contents, click, change actions document.getElementById("res_date").innerHTML = this.response; select = document.querySelector("#res_date select.month"); select.addEventListener("change", res.cal); select = document.querySelector("#res_date select.year"); select.addEventListener("change", res.cal); select = document.querySelectorAll("#res_date .pick, #res_date .active"); for (var i of select) { i.addEventListener("click", res.pick); } // Load time slots res.slot(); }; xhr.send(data); }, slot : function () { // res.slot() : load time slot selector // Disable submit first document.getElementById("res_go").disabled = true; // Selected date var select = document.querySelector("#res_date td.active").innerHTML; if (select.length==1) { select = "0" + select; } select = document.querySelector("#res_date select.month").value + "-" + select; select = document.querySelector("#res_date select.year").value + "-" + select; // AJAX data var data = new FormData(); data.append('req', 'show-slot'); data.append('date', select); // AJAX call var xhr = new XMLHttpRequest(); xhr.open('POST', "ajax-reserve.php", true); xhr.onload = function(){ // Set contents document.getElementById("res_slot").innerHTML = this.response; // Enable submit document.getElementById("res_go").disabled = false; }; xhr.send(data); }, pick : function () { // res.pick() : change selected date var select = document.querySelector("#res_date .active"); if (select!=this) { select.classList.remove("active"); select.classList.add("pick"); this.classList.remove("pick"); this.classList.add("active"); res.slot(); } }, save : function () { // res.save() : save the reservation // Selected date var select = document.querySelector("#res_date td.active").innerHTML; if (select.length==1) { select = "0" + select; } select = document.querySelector("#res_date select.month").value + "-" + select; select = document.querySelector("#res_date select.year").value + "-" + select; // AJAX data var data = new FormData(); data.append('req', 'book-slot'); data.append('name', document.getElementById("res_name").value); data.append('email', document.getElementById("res_email").value); data.append('tel', document.getElementById("res_tel").value); data.append('notes', document.getElementById("res_notes").value); data.append('date', select); data.append('slot', document.querySelector("#res_slot select").value); // AJAX call var xhr = new XMLHttpRequest(); xhr.open('POST', "ajax-reserve.php", true); xhr.onload = function(){ var res = JSON.parse(this.response); // OK - Redirect to thank you page if (res.status==1) { location.href = "thank-you.html"; } // ERROR - show error else { alert(res.message); } }; xhr.send(data); return false; } }; window.addEventListener("load", res.cal);
Complicated Javascript? Nope. There are only 4 AJAX functions here.
Function Description res.cal() Fires up when the page is initially loaded, and whenever the user changes the month/year. It will do an AJAX call to the server to render the calendar for the selected period. res.slot() Follows up after res.cal, or whenever the user chooses a different date. This will load the timeslot selector via an AJAX call. res.pick() Fires up when the user picks a different date. Updates the HTML interface, and calls the res.slot function to refresh the available timeslots for the selected date. res.save() Proceed to process the reservation. DATE RANGE BOOKING
THE HTML
reserve-range.htmlPHP Reservation Demo - Date Range Booking RESERVATION
Simple HTML form here, but with a few small twists:
- The start and end date selectors will be loaded via AJAX.
- The submit button is disabled by default. We will enable it only when both date selectors are loaded.
THE JAVASCRIPT
public/reserve-range.jsvar res = { calstart : function () { // res.calstart() : show calendar for date start res.cal("start"); }, calend : function () { // res.calend() : show calendar for date end res.cal("end"); }, cal : function (target) { // res.cal() : show calendar // target : start or end // Disable submit first document.getElementById("res_go").disabled = true; // Target event handlers var calchange = target=="start" ? res.calstart : res.calend ; var picker = target=="start" ? res.pickstart : res.pickend ; // AJAX data var data = new FormData(); data.append('req', 'show-cal'); // Get selected month & year - If they exist var select = document.querySelector("#res_" + target +" select.month"); if (select!=null) { data.append('month', select.value); select = document.querySelector("#res_" + target + " select.year"); data.append('year', select.value); } // AJAX call var xhr = new XMLHttpRequest(); xhr.open('POST', "ajax-reserve.php", true); xhr.onload = function(){ // Set contents, click, change actions document.getElementById("res_" + target).innerHTML = this.response; select = document.querySelector("#res_" + target + " select.month"); select.addEventListener("change", calchange); select = document.querySelector("#res_" + target + " select.year"); select.addEventListener("change", calchange); select = document.querySelectorAll("#res_" + target + " .pick, #res_" + target + " .active"); for (var i of select) { i.addEventListener("click", picker); } // Enable submit document.getElementById("res_go").disabled = false; }; xhr.send(data); }, pickstart : function () { // res.pickstart() : change selected date for date start res.pick('start', this); }, pickend : function () { // res.pickend() : change selected date for date end res.pick('end', this); }, pick : function (target, picked) { // res.pick() : change selected date // target : start or end // picked : current element being clicked on var select = document.querySelector("#res_" + target + " .active"); if (select!=picked) { select.classList.remove("active"); select.classList.add("pick"); picked.classList.remove("pick"); picked.classList.add("active"); } }, save : function () { // res.save() : save the reservation // Selected start date var start = document.querySelector("#res_start td.active").innerHTML; if (start.length==1) { start = "0" + start; } start = document.querySelector("#res_start select.month").value + "-" + start; start = document.querySelector("#res_start select.year").value + "-" + start; // Selected end date var end = document.querySelector("#res_end td.active").innerHTML; if (end.length==1) { end = "0" + end; } end = document.querySelector("#res_end select.month").value + "-" + end; end = document.querySelector("#res_end select.year").value + "-" + end; // End date must be after start date if (Date.parse(start)>=Date.parse(end)) { alert("End date cannot be earlier than start date!"); } else { // AJAX data var data = new FormData(); data.append('req', 'book-range'); data.append('name', document.getElementById("res_name").value); data.append('email', document.getElementById("res_email").value); data.append('tel', document.getElementById("res_tel").value); data.append('notes', document.getElementById("res_notes").value); data.append('start', start); data.append('end', end); // AJAX call var xhr = new XMLHttpRequest(); xhr.open('POST', "ajax-reserve.php", true); xhr.onload = function(){ var res = JSON.parse(this.response); // OK - Redirect to thank you page if (res.status==1) { location.href = "thank-you.html"; } // ERROR - show error else { alert(res.message); } }; xhr.send(data); } return false; } }; window.addEventListener("load", function(){ res.calstart(); res.calend(); });
Function Description res.cal() Loads the calendar via AJAX. Either for the start or end date. res.calstart() Fires up on page load, or when the user changes the month/date of the start date. Calls res.cal to load the calendar. res.calend() Fires up on page load, or when the user changes the month/date of the end date. Calls res.cal to load the calendar. res.pick() Updates the HTML calendar to show a newly selected date. res.pickstart() Fires when the user clicks on a date on the start date. Calls res.pick to update the interface. res.pickend() Fires when the user clicks on a date on the end date. Calls res.pick to update the interface. res.save() Proceed to save the reservation on the server. THE CSS
public/theme.css/* [CALENDAR] */ /* Month & year selector */ div.calendar select { width: 50% !important; padding: 5px } div.calendar table { width: 100%; border-collapse: seperate; margin-top: 10px; } /* All the cells in general */ div.calendar table td { width: 14%; padding: 5px; text-align: center; } /* First row - days of week */ div.calendar table tr.days td { background: #888; color: #fff; font-weight: bold; } /* Currently chosen date */ div.calendar table td.active { background: #d64646; color: #fff; } /* Dates you can choose */ div.calendar table td.pick:hover { cursor: pointer; background: #ffe5d3; } /* Blank cells */ div.calendar td.blank { background: #ddd; } /* [DOES NOT MATTER] */ html, body { font-family: arial, sans-serif; } #res_form { padding: 15px; max-width: 400px; background: #f2f2f2; } #res_form label, #res_form input, #res_form button, #res_form select { font-size: 16px; width: 100%; box-sizing: border-box; } #res_form label, #res_form input, #res_form button { display: block; } #res_form label { color: #555; margin: 5px 0; } #res_form input, #res_form select { padding: 5px; } #res_form button { margin-top: 10px; background: #b53732; color: #fff; padding: 10px; border: 0; }
Just some cosmetics for this example, the only usable part for your project is probably the styles of the calendar.
THANK YOU PAGE
thank-you.htmlPHP Reservation Demo - Thank You Page THANK YOU!
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mollis erat nec tortor efficitur, non pulvinar ligula scelerisque.
Vestibulum facilisis quis quam id lobortis. Nullam bibendum aliquet ultrices. Aliquam elementum neque quis massa tristique cursus.
Well, a thank-you page… Please do make a proper one for yourself.
EXTRA
DOWNLOAD & MOREThat’s all for the code, and here is the download link as promised – Plus a few small extras that may be useful to you.
TODO LIST
That’s it for this simple system, but it is still a skeleton. Your client or business probably has 101 more different requirements, so here is a small list of stuff that you will want to build on top of this guide:
- Beef up the reservation library and AJAX handler – Put in your own restrictions and rules on the reservation dates and slots.
- Close the loop – What happens after the user submits the reservation. Send an email? Call them to confirm? Check your own availability first? Ask for deposit payment?
- Quite a lot of cosmetics – To fit your own project.
- Proper integration into your existing website or build a new admin panel.
- Complete administration and reports – Extract the reservations for a given period, export to spreadsheet, etc…
ADDITIONAL FIELDS OR MODIFICATIONS?
So you want to add some fields, or maybe make some modifications to this system? Just remember the 3 parts of this guide, tackle them piece-by-piece and you will do just fine:
- Database – First update the database table, create or remove the fields that you want.
- Server-side script – Update the reservation library file, create your own functions if you must. Also, remember to update the AJAX handler script to include your new fields.
- Client-side script – Finally, update the HTML and Javascript.
ADMIN PANEL & LOGIN
For you guys who start out on a fresh project. You may have a booking system, but it still needs a proper user database, login mechanism, and admin panel –
3 Steps Simple PHP Admin Panel (Source Code Included)
SOURCE CODE DOWNLOAD
Click here to download the source code, I have released it under the MIT license, so feel free to build on top of it or use it in your own project.
CLOSING
WHAT’S NEXT?Thank you for reading, and we have come to the end of this guide. I hope that it has helped you in your project, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!
The post 3 Steps Simple PHP Reservation System appeared first on Code Boxx.
- Following up, the timeslot selector will be loaded into