온라인 예약
간단한 PHP 예약 시스템을 만드는 방법에 대한 자습서에 오신 것을 환영합니다.
그래서 당신은 서비스를 제공하고 온라인 예약을 위해 개방하고 싶습니까?
아니면 온라인으로 대여 가능한 방을 열고 싶습니까? 두려워하지 마십시오 –이 안내서는 예약 시스템을 만드는 방법에 대한 단계를 안내합니다.
ⓘ이 튜토리얼의 시작 부분에 모든 소스 코드가 포함 된 zip 파일이 포함되어 있으므로 모든 것을 복사하여 붙여 넣을 필요가 없습니다.
소스 코드 다운로드
먼저 약속 한대로 소스 코드에 대한 다운로드 링크가 있습니다.
소스 코드 다운로드
소스 코드를 다운로드하려면 여기를 클릭하십시오. MIT 라이센스에 따라 릴리스되었으므로 그 위에 빌드하거나 자신의 프로젝트에서 자유롭게 사용하십시오.
폴더
다음은 zip 파일에서 폴더를 구성하는 방법에 대한 간략한 소개입니다.
빠른 시작
개요 및 가정
실제 스크립트로 들어가기 전에 간단한 시스템 개요에 대한 작은 섹션과 몇 가지 가정을 들었습니다. 따라서 이 안내서에서 무엇을 기대해야 하는지 알고 있습니다.
가정 및 참고 사항
여기에 있는 여러분 중 일부는 기존 프로젝트 나 웹 사이트를 가지고 있고 그 위에 예약 시스템을 구축 할 계획입니다. 그래서 우리는 바퀴를 재발 명하지 않고 기존의 것 위에 다른 관리자 패널을 만들지 않을 것입니다. 이것은 "예약 전용"이며, 새로 시작하는 사용자에게는 아래의 추가 섹션에 관리자 패널을 만드는 방법에 대한 링크가 있습니다.
또한 jQuery, Bootstrap, Angular, Cake, Symfony 등과 같은 타사 프레임 워크는 없습니다.이 가이드는 순수한 HTML, CSS, Javascript 및 PHP로 제공됩니다. 그것은 여러분들이 더 쉽게 통합 할 수 있도록 해야 합니다.
개요
이 프로젝트에는 3 가지 부품이 있습니다 :
이제 여기의 모든 사람은 하루 종일 예약, 날짜 범위 예약, 시간대 예약, 다음 날만, 30 일 이내에 등 다른 예약 요구 사항을 가지고 있을 수 있습니다. 모든 가능한 경우를 처리하는 것은 불가능 하므로 이 안내서 만 가장 일반적인 시나리오 3 개 –
특정 규칙과 제한 사항을 직접 구현해야 합니다.
데이터베이스
이제 모든 기본 사항을 벗어 났으므로 이제 데이터베이스 기반 예약 테이블을 만들어 프로젝트의 기초를 구축해 보겠습니다.
예약 표
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;
필드
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. |
서버 측 스크립트
데이터베이스 기반을 완성 했으므로 예약을 처리 할 서버 측 스크립트를 작성해 보겠습니다.
구성 파일
// MUTE NOTICES error_reporting(E_ALL & ~E_NOTICE); // DATABASE SETTINGS - CHANGE THESE TO YOUR OWN define('DB_HOST', 'localhost'); define('DB_NAME', 'test'); define('DB_CHARSET', 'utf8'); define('DB_USER', 'root'); define('DB_PASSWORD', ''); // FILE PATH define('PATH_LIB', __DIR__ . DIRECTORY_SEPARATOR);
먼저, 모든 데이터베이스와 설정을 안전하게 유지하기 위해 구성 파일을 만들어 보겠습니다. – 데이터베이스 설정을 자신의 것으로 변경해야 합니다.
예약 라이브러리
class Res { /* [DATABASE HELPER FUNCTIONS] */ protected $pdo = null; protected $stmt = null; public $error = ""; public $lastID = null; function __construct() { // __construct() : connect to the database // PARAM : DB_HOST, DB_CHARSET, DB_NAME, DB_USER, DB_PASSWORD // ATTEMPT CONNECT try { $str = "mysql:host=" . DB_HOST . ";charset=" . DB_CHARSET; if (defined('DB_NAME')) { $str .= ";dbname=" . DB_NAME; } $this->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; } /* [WHOLE DAY BOOKING] */ function bookDay ($name, $email, $tel, $date, $notes="") { // bookDay() : reserve for the entire day // Check if customer already booked on the day $sql = "SELECT * FROM `reservations` WHERE `res_email`=? AND `res_date`=?"; $cond = [$email, $date]; $check = $this->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 ; } }
이 라이브러리는 처음에는 방대해 보이지만 두 섹션으로 나뉩니다.
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 |
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
// INIT require __DIR__ . DIRECTORY_SEPARATOR . "lib" . DIRECTORY_SEPARATOR . "2a-config.php"; require PATH_LIB . "2b-lib-res.php"; $reslib = new Res(); /* ANTI-SPAM MEASURE YOU CAN CONSIDER * ONLY ALLOW REGISTERED USERS TO BOOK * YOU CAN DO SOMETHING LIKE THIS -> 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']<=12) ? $_POST['month'] : date("n"); $thisYear = is_numeric($_POST['year']) ? $_POST['year'] : date("Y"); $thisStart = strtotime(sprintf("%s-%02u-01", $thisYear, $thisMonth)); $daysInMonth = date("t", $thisStart); $thisEnd = strtotime(sprintf("%s-%02u-%s", $thisYear, $thisMonth, $daysInMonth)); $startDay = date("N", $thisStart); $endDay = date("N", $thisEnd); $yearNow = date("Y"); $monthNow = date("n"); $dayNow = date("j"); // Calculate calendar squares $squares = []; // If the first day of the month is not Sunday, pad with blanks if ($startDay != 7) { for ($i=0; $i<$startDay; $i++) { $squares[] = ["b"=>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<=$dayNow; $inow++) { $squares[] = ["d"=>$inow, "b"=>1]; } } // Populate the rest of the selectable days for ($inow; $inow<=$daysInMonth; $inow++) { $squares[] = ["d"=>$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<=$daysInMonth; $inow++) { if ($dayNow==6 || $dayNow==7) { $squares[] = ["d"=>$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; $i<$blanks; $i++) { $squares[] = ["b"=>1]; } } // 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 "<select class='month'>"; // Does not allow months that have already passed this year for ($i=($yearNow==$thisYear ? $monthNow : 1); $i<=12; $i++) { printf("<option value='%02u'%s>%s</option>", $i, $i==$thisMonth?" selected":"", $months[$i-1] ); } echo "</select>"; // Year selector echo "<select class='year'>"; // Set to max 3 years from now - change it if you want for ($i=$yearNow; $i<=$yearNow+3; $i++) { printf("<option value='%s'%s>%s</option>", $i, $i==$thisYear?" selected":"", $i ); } echo "</select>"; // Dates echo "<table><tr class='days'>"; // First row - Days of week $days = ["Sun", "Mon", "Tue", "Wed", "Thur", "Fri", "Sat"]; foreach ($days as $d) { echo "<td>$d</td>"; } echo "</tr><tr>"; // Following rows - Days in month $total = count($squares); $first = true; for ($i=0; $i<$total; $i++) { echo "<td class='"; if ($squares[$i]['b']) { echo "blank"; } else if ($first) { echo "active"; $first = false; } else { echo "pick"; } echo "'>"; if ($squares[$i]['d']) { echo $squares[$i]['d']; } echo "</td>"; if ($i!=0 && ($i+1)%7==0) { echo "</tr><tr>"; } } echo "</tr></table>"; 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 } */ <select> <option value="AM">AM</option>> <option value="PM">PM</option>> </select> break; // ADD NEW RESERVATION - WHOLE DAY BOOKING case "book-day": // Save reservation to database $pass = $reslib->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("admin@yoursite.com", "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("admin@yoursite.com", "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("admin@yoursite.com", "Reservation receieved", $message); } */ // Server response echo json_encode([ "status" => $pass ? 1 : 0, "message" => $pass ? "OK" : $reslib->error ]); break; }}
마지막으로, 라이브러리 파일은 자체적으로 아무것도 하지 않으므로 이 미친 것처럼 보이는 AJAX 핸들러 스크립트의 이유는… 혼란스러워 보이지만 실제로는 아무 것도 아닙니다.
아약스 란?!
AJAX 핸들러의 작동 방식은 매우 간단합니다. 처리하려는 대상에 대해 이 스크립트에 $_POST['req'] 요청을 보내고 필요한 매개 변수가 옵니다.
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. |
따라서 이 스크립트에서 사용하지 않는 일부 섹션을 실제로 제거 할 수 있으며 실제로 그렇게 복잡하지는 않습니다.
THE CRAZY CALENDAR
서버 측 스크립트의 가장 열악한 부분은 아마도 주어진 달에 대해 선택 가능한 날짜를 생성하는 것입니다. 여기서는 AJAX 핸들러의 이 섹션을 자세히 살펴볼 것입니다.
클라이언트 측 스크립트
마지막으로 실제 HTML 예약 페이지 자체를 구축하여 퍼즐 만 완성하면 됩니다. 여기서 3 가지 시나리오를 각각 실행하겠습니다 ... 적용되지 않는 시나리오는 건너 뛰십시오.
하루 종일 예약
HTML
<html> <head> <title> PHP Reservation Demo - Single Full Day Booking </title> <script src="public/3a-reserve-day.js"></script> <link href="public/3-theme.css" rel="stylesheet"> </head> <body> <h1> RESERVATION </h1> <form id="res_form" onsubmit="return res.save()"> <label for="res_name">Name</label> <input type="text" required id="res_name"/> <label for="res_email">Email</label> <input type="email" required id="res_email"/> <label for="res_tel">Telephone Number</label> <input type="text" required id="res_tel"/> <label for="res_notes">Notes (if any)</label> <input type="text" id="res_notes"/> <label>Reservation Date</label> <div id="res_date" class="calendar"></div> <button id="res_go" disabled> Submit </button> </form> </body> </html>
이것은 이해하기 쉬워야 합니다 – 단지 일반적인 HTML 예약 양식입니다. 그러나 여기 두 가지를 주의하십시오.
자바 스크립트
var 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', "2c-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', "2c-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 = "3d-thank-you.html"; } // ERROR - show error else { alert(res.message); } }; xhr.send(data); return false; } }; window.addEventListener("load", res.cal);
이 자바 스크립트는 복잡해 보이지만 실제로는 3 개의 함수 만 있습니다.
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. |
타임 슬롯 예약
HTML
<html> <head> <title> PHP Reservation Demo - Single Time Slot Booking </title> <script src="public/3b-reserve-slot.js"></script> <link href="public/3-theme.css" rel="stylesheet"> </head> <body> <h1> RESERVATION </h1> <form id="res_form" onsubmit="return res.save()"> <label for="res_name">Name</label> <input type="text" required id="res_name"/> <label for="res_email">Email</label> <input type="email" required id="res_email"/> <label for="res_tel">Telephone Number</label> <input type="text" required id="res_tel"/> <label for="res_notes">Notes (if any)</label> <input type="text" id="res_notes"/> <label>Reservation Date</label> <div id="res_date" class="calendar"></div> <label>Reservation Slot</label> <div id="res_slot"></div> <button id="res_go" disabled> Submit </button> </form> </body> </html>
타임 슬롯 예약의 HTML은 하루 종일과 거의 동일합니다. – 이것은 단순한 HTML 양식이지만 참고하십시오.
자바 스크립트
var 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', "2c-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', "2c-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', "2c-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 = "3d-thank-you.html"; } // ERROR - show error else { alert(res.message); } }; xhr.send(data); return false; } }; window.addEventListener("load", res.cal);
복잡한 자바 스크립트? 아니. 여기에는 4 개의 AJAX 기능 만 있습니다.
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. |
날짜 범위 예약
HTML
<html> <head> <title> PHP Reservation Demo - Date Range Booking </title> <script src="public/3c-reserve-range.js"></script> <link href="public/3-theme.css" rel="stylesheet"> </head> <body> <h1> RESERVATION </h1> <form id="res_form" onsubmit="return res.save()"> <label for="res_name">Name</label> <input type="text" required id="res_name" value="John Doe"/> <label for="res_email">Email</label> <input type="email" required id="res_email" value="john@doe.com"/> <label for="res_tel">Telephone Number</label> <input type="text" required id="res_tel" value="123456"/> <label for="res_notes">Notes (if any)</label> <input type="text" id="res_notes" value="test"/> <label>Reservation Start</label> <div id="res_start" class="calendar"></div> <label>Reservation End</label> <div id="res_end" class="calendar"></div> <button id="res_go" disabled> Submit </button> </form> </body> </html>
여기에 간단한 HTML 양식이 있지만 약간의 왜곡이 있습니다.
자바 스크립트
var 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', "2c-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', "2c-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 = "3d-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. |
CSS
/* [CALENDAR] */ /* Month & year selector */ div.calendar select { width: 50% ; 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; }
이 예제에서 일부 화장품은 프로젝트에서 사용할 수 있는 유일한 부분은 아마도 달력의 스타일 일 것입니다.
감사합니다 페이지
<html> <head> <title> PHP Reservation Demo - Thank You Page </title> <link href="public/3-theme.css" rel="stylesheet"> </head> <body> <h1> THANK YOU! </h1> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mollis erat nec tortor efficitur, non pulvinar ligula scelerisque. </p> <p> Vestibulum facilisis quis quam id lobortis. Nullam bibendum aliquet ultrices. Aliquam elementum neque quis massa tristique cursus. </p> </body> </html>
감사 페이지… 자신을 위해 적절한 페이지를 만드십시오.
유용한 비트
이것으로 코드가 완성되었으며 여기에 유용한 몇 가지 추가 기능이 있습니다.
할 일 목록
이것이 이 단순한 시스템에 대한 것이지만 여전히 골격입니다. 고객 또는 비즈니스에 101 가지 더 다양한 요구 사항이 있을 수 있으므로 다음은 이 안내서를 기반으로 작성하려는 작은 목록입니다.
추가 분야 또는 수정?
필드를 추가하거나 이 시스템을 수정하고 싶습니까? 이 안내서의 3 가지 부분 만 기억하고 하나씩 해결하면 괜찮습니다.
관리자 패널 및 로그인
새로운 프로젝트를 시작하는 여러분에게. 예약 시스템이 있을 수 있지만 여전히 적절한 사용자 데이터베이스, 로그인 메커니즘 및 관리자 패널이 필요합니다.
등록된 댓글이 없습니다.