Reputation: 55
I have this table:
BookingID userID VehicleId FromDate ToDate Comments TotalPrice Status
9 14 1015 2021-08-03 2021-08-08 258 0
10 14 1015 2021-08-10 2021-08-12 129 0
11 14 1015 2021-08-13 2021-08-14 129 0
12 14 1015 2021-08-18 2021-08-23 129 0
And this PHP function:
function isBooked($VehicleID, $fromDate, $toDate) {
$sql = "SELECT BookingID, VehicleID, FromDate, ToDate FROM tbl_bookings WHERE VehicleID = $VehicleID";
$con = new Database_Connector();
$result = $con->read_records($sql);
$flag = 0;
while ($row = mysqli_fetch_array($result) && $flag == 0)
{
$bFromDate = $row['FromDate'];
$bToDate = $row['ToDate'];
if (($fromDate >= $bFromDate) && ($toDate <= $bToDate)){
$flag = 1;
}
}
if ($flag==1) {
echo "Not available";
} else { echo "is Available"; }
}
What I need is on the given dates $fromDate and $toDate to check if they are records in the booking table on that specific date range.
Idealy given:
My code from what I can understand checks only the set of dates of each row. Hence I receive the wrong results. Anyone can propose a better solution? I'm open to totally different approaches.
UPDATE:
function isBooked($VehicleID, $fromDate, $toDate) {
$sql= "SELECT CASE WHEN y.BookingID IS NOT NULL AND VehicleID=$VehicleID THEN '0' ELSE '1' END availability
FROM (SELECT 1)x LEFT JOIN tbl_bookings y ON FromDate <= '$toDate' AND ToDate > '$fromDate'";
echo $sql;
$con = new Database_Connector();
$result = $con->read_records($sql);
while ($row = mysqli_fetch_array($result)){
$variable = $row['availability'];
}
if ($variable == 0) {
return 0;
}elseif ($variable == 1) {
return 1;
}else {
return "error";
}
}
function addBooking ($VehicleID, $fromDate, $toDate, $message, $userID) {
$status = 0;
$days = (round ((strtotime($toDate) - strtotime($fromDate)) / (60 * 60 *24)))+1;
$vPrice = getVehiclePrice($VehicleID);
$TotalPrice = $vPrice * $days;
$sql= "INSERT INTO tbl_bookings(BookingID, userID, VehicleId, FromDate, ToDate, Comments, TotalPrice, Status, PostingDate, LastUpdationDate) VALUES (DEFAULT, '$userID', '$VehicleID', '$fromDate', '$toDate', '$message', '$TotalPrice', '$status', DEFAULT, DEFAULT)";
$con = new Database_Connector();
$result = $con->execute_query($sql);
if($result != FALSE){
return "Booking submited successful";
}else{
return "Something went wrong. Please try again";
}
}
function generateBookings($x) {
$i=1;
while ($i <= $x) {
$days = mt_rand(1, 10);
$daysText = '+ '. $days. ' days';
$min_date = strtotime(date("Y-m-d"));
$max_date = strtotime(date('2021-12-30'));
$rand_start_date = date('Y-m-d', rand($min_date, $max_date));
$rand_end_date = date('Y-m-d', strtotime($rand_start_date. $daysText));
$rand_VehicleID = mt_rand(1001, 1020);
$rand_UserID = mt_rand(17, 25);
$message = "Lorem Ipsum Dollor";
if (isBooked($rand_VehicleID, $rand_start_date, $rand_end_date) == 1 ) {
addBooking ($rand_VehicleID, $rand_start_date, $rand_end_date, $message, $rand_UserID);
$i = $i+1;
}
}
}
sqlfiddle: http://sqlfiddle.com/#!9/67c994/1/0
Upvotes: 1
Views: 545
Reputation:
For instance:
DROP TABLE IF EXISTS OscarCy;
CREATE TABLE OscarCy
(BookingID INT AUTO_INCREMENT PRIMARY KEY
,FromDate DATE NOT NULL
,ToDate DATE NOT NULL
);
INSERT INTO OscarCy VALUES
( 9,'2021-08-03','2021-08-08'),
(10,'2021-08-10','2021-08-12'),
(11,'2021-08-13','2021-08-14'),
(12,'2021-08-18','2021-08-23');
...
SET @range_start = '2021-08-15';
SET @range_end = '2021-08-29';
SELECT CASE WHEN y.bookingid IS NOT NULL THEN 'Not Available' ELSE 'Available' END availability
FROM (SELECT 1)x
LEFT
JOIN OscarCy y
ON fromdate <= @range_end
AND todate > @range_start;
+---------------+
| availability |
+---------------+
| Not Available |
+---------------+
SET @range_start = '2021-08-15';
SET @range_end = '2021-08-17';
SELECT CASE WHEN y.bookingid IS NOT NULL THEN 'Not Available' ELSE 'Available' END availability
FROM (SELECT 1)x
LEFT
JOIN OscarCy y
ON fromdate <= @range_end
AND todate > @range_start;
+--------------+
| availability |
+--------------+
| Available |
+--------------+
Note that the only other answer thus far provided appears to use an incorrect comparison for overlaps. I'd comment, but I don't have the rep.
EDIT: With reference to your comment, I don't understand it. Here's the subset of data for VehicleId 1020, and where 'x' represents bookings that overlap with the give date range...
+-----------+--------+-----------+------------+------------+--------------------+------------+------+
| BookingID | userID | VehicleId | FromDate | ToDate | Comments | TotalPrice | x |
+-----------+--------+-----------+------------+------------+--------------------+------------+------+
| 296 | 22 | 1020 | 2021-08-11 | 2021-08-12 | Lorem Ipsum Dollor | 3000 | 0 |
| 313 | 24 | 1020 | 2021-08-31 | 2021-09-08 | Lorem Ipsum Dollor | 13500 | 0 |
| 326 | 17 | 1020 | 2021-11-12 | 2021-11-22 | Lorem Ipsum Dollor | 16500 | 1 |
| 335 | 21 | 1020 | 2021-09-20 | 2021-09-29 | Lorem Ipsum Dollor | 15000 | 0 |
+-----------+--------+-----------+------------+------------+--------------------+------------+------+
I can't see that there are any other 'overlaps' here.
Upvotes: 1
Reputation: 2191
First of all, you have a potential SQL injection vulnerability in your code:
//THIS IS DANGEROUS
$sql = "SELECT BookingID, VehicleID, FromDate, ToDate FROM tbl_bookings WHERE VehicleID = $VehicleID";
$con = new Database_Connector();
$result = $con->read_records($sql);
//DO SOMETHING LIKE THIS INSTEAD
$sql = "SELECT BookingID, VehicleID, FromDate, ToDate FROM tbl_bookings WHERE VehicleID = :vehicleID";
$con = new Database_Connector();
$result = $con->read_records($sql, ['vehicleID' => $VehicleID]);
If $VehicleID
ends up as something like 1; DROP TABLE tbl_bookings;--
, you could find yourself having a really, really bad day.
You should be parameterising your queries to protect against these kinds of (unfortunately exceedingly common) attacks.
To answer your actual question, I'd probably offload the date checking to the database, using something like this:
function isBooked($VehicleID, $fromDate, $toDate) {
$sql = "SELECT BookingID, VehicleID, FromDate, ToDate FROM tbl_bookings WHERE VehicleID = :VehicleID and (FromDate >= :FromDate and ToDate <= :ToDate";
$con = new Database_Connector();
$results = $con->read_records($sql, ['VehicleID' => $VehicleID, 'FromDate' => $fromDate, 'ToDate' => $toDate]);
if (empty($results)) {
echo "Available";
} else {
echo "Not Available";
}
}
I'm unsure what type $con->read_records
returns, so I don't know if an empty()
check would work. You may have to use $results->rowCount() === 0
instead.
Upvotes: 0