I had to create my own test data (thanks for that) but naturally I don't know how it conforms with yours.
TABLE: product TABLE: bookingItem
+----+-------------+-----------+--------+ +----+-----------+---------------------+---------------------+----------+
| id | productName | category | status | | id | productid | startTime | endTime | quantity |
+----+-------------+-----------+--------+ +----+-----------+---------------------+---------------------+----------+
| 1 | Room 1 | Guestroom | Active | | 1 | 1 | 2024-01-01 11:32:01 | 2024-01-02 11:32:59 | 1 |
| 2 | Room 2 | Guestroom | Active | | 2 | 2 | 2024-02-01 11:34:08 | 2024-02-03 11:34:24 | 2 |
| 3 | Room 3 | Guestroom | Active | | 3 | 3 | 2024-03-01 11:34:56 | 2024-03-04 11:35:08 | 3 |
| 4 | Room 4 | Guestroom | NULL | | 4 | 2 | 2024-04-01 12:20:20 | 2024-04-07 12:20:41 | 6 |
| 5 | Room 5 | Guestroom | NULL | | 5 | 3 | 2024-05-01 01:21:49 | 2024-05-05 12:21:58 | 4 |
+----+-------------+-----------+--------+ | 6 | 5 | 2024-06-19 12:23:03 | 2024-06-29 12:23:28 | 10 |
| 7 | 2 | 2024-06-01 13:02:51 | 2024-06-15 13:03:16 | 14 |
+----+-----------+---------------------+---------------------+----------+
On running your code with my data I get these results for Q1 and Q2. I have written the correct totals in red. As you can see there is a distinct pattern - your totals are the correct totals squared. However, I could not spot any multiplication in the code
(I ran as separate query to confirm the correct totals)
I have to say, in your code you really make a meal of those dates in the years and quarters considering that SQL can handle it easily. Here's my version...
<?php
###############################################
# CREATE YOUR OWN PDO DATABASE CONNECTION #
# #
require 'db_inc.php';
$pdo = mdbConnect('db1');
# #
# #
###############################################
$range = [ '2020-01-01', '2024-07-31' ];
$selectedYear = $_GET['year'] ?? 0;
$whereYear = '';
if ($selectedYear) {
$whereYear = 'AND YEAR(d.dt) = ?';
$range[] = $selectedYear;
}
$res = $pdo->prepare("WITH RECURSIVE dates(dt) AS
(
SELECT ?
UNION ALL
SELECT dt + INTERVAL 1 MONTH
FROM dates WHERE dt < ?
)
SELECT
YEAR(d.dt) AS yr
, QUARTER(d.dt) as qtr
, MONTHNAME(dt) AS mth
, productName AS room
, COALESCE(SUM(DATEDIFF(endTime, startTime)), '-') AS nights
FROM product p
CROSS JOIN dates d
LEFT JOIN bookingitem b ON b.productid = p.id
AND YEAR(d.dt) = YEAR(b.startTime)
AND MONTH(d.dt) = MONTH(b.startTime)
WHERE p.`status` = 'Active'
$whereYear
GROUP BY yr, qtr, MONTH(d.dt), p.id
");
$res->execute($range);
$results = $res->fetchAll();
$rooms = array_unique(array_column($results, 'room'));
$theads = "<tr><th>Quarter</th><th>Month</th><th>" . join('</th><th>', $rooms) . "</th><th>Total</th></tr>\n";
### RESTRUCTURE THE RESULTS ARRAY
foreach ($results as $r) {
$data[$r['yr']][$r['qtr']][$r['mth']][$r['room']] = $r['nights'];
}
?>
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset="utf-8">
<title>Example</title>
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
<style type='text/css'>
table {
width: 100%;
border-collapse: collapse;
}
th {
background-color: #808080;
color: white;
padding: 8px;
}
td {
padding: 4px 12px;
text-align: right;
}
.ca {
text-align: center;
background-color: #EEE;
}
.la {
text-align: left;
background-color: #EEE;
color: black;
}
</style>
</head>
<body>
<header class='w3-indigo w3-padding w3-margin-bottom'>
<h1>Guestroom Occupancy</h1>
</header>
<div class='w3-content w3-padding'>
<?php
## OUTPUTFROM RESTRUCTURED ARRAY
foreach ($data as $yr => $ydata) {
echo "<h3>$yr</h3>\n
<table border='1'>
$theads
";
foreach ($ydata as $qtr => $qdata) {
$span = 3 + count($rooms);
echo "<tr><th class='la' colspan='$span'>Quarter {$qtr}</th></tr>\n";
foreach ($qdata as $mth => $mdata) {
echo "<tr><td> </td><td>$mth</td><td>" . join('</td><td>', $mdata) . "</td><td><b>" . array_sum($mdata) . "</b></td></tr>\n";
}
}
echo "</table>\n";
}
?>
</div>
</body>
</html>
Output