Well it is possible to query the second special prices table as a join to the first table on the year and month fields, just to need to use CONCAT to make a year-month value to compare to stand date fields. What is really hindering things is the GROUP BY used in the query as you will never get multiple special pricing results. If we remove the GROUP BY we can join the tables as follows grabbing data from both tables.
FROM `standard_prices` AS p
LEFT JOIN `special_prices` AS s
ON CONCAT(p.year, '-', LPAD(CONVERT(p.month_numbers, UNSIGNED INTEGER), 2, '0')) BETWEEN DATE_FORMAT(s.date_start,'%Y-%m') AND DATE_FORMAT(s.date_end,'%Y-%m')
ORDER BY p.year, CONVERT(p.month_numbers, UNSIGNED INTEGER)
As before we are setting a value for day of the week based on what part of the week each record holds, i.e. if record is for Friday, friday = price otherwise friday = 0; In the old query we used MAX and GROUP BY to then get the highest value. With the new joined query we will need to do something a little different to group things by Year, Month, Day of the week, plus handle multiple records for these days, let alone deal with special pricing.
This is where a data array can really come in handy.
This order of Year, Month, Day of the week and multiple records per day is a simple TREE like structure. Much like your PC has folders within folders you would build a data array starting from the outside.
$data = array();
Within the WHILE loop we place the Year as the primary array key.
$data[$row['year']]
We then place the Month as the secondary array key.
$data[$row['year']][$row['month_numbers']]
And as we gathering information from 2 DB tables for any given month i.e. Specials or those of the Week, we will use these named key(s) to keep things straight.
$data[$row['year']][$row['month_numbers']]['week']
OR
$data[$row['year']][$row['month_numbers']]['specials']
Under the WEEK key we have our 4 subweek keys for midweek, friday, saturday and sunday.
$data[$row['year']][$row['month_numbers']]['week']['midweek']
$data[$row['year']][$row['month_numbers']]['week']['friday']
$data[$row['year']][$row['month_numbers']]['week']['saturday']
$data[$row['year']][$row['month_numbers']]['week']['sunday']
Under each of these days we will have multiple records so we will use the record ID as the key and corresponding result FIELD as the value.
$data[$row['year']][$row['month_numbers']]['week']['midweek'][$row['id']] = $row['midweek'];
$data[$row['year']][$row['month_numbers']]['week']['friday'][$row['id']] = $row['friday'];
$data[$row['year']][$row['month_numbers']]['week']['saturday'][$row['id']] = $row['saturday'];
$data[$row['year']][$row['month_numbers']]['week']['sunday'][$row['id']] = $row['sunday'];
Now our query will only return results on Special Pricing if the query conditions match, so in our DATA building we only want to add values to our array IF we have query result data. We can check for a special ID and if not empty build the array for specials with the fields from that table.
if(!empty($row['specialID'])):
$data[$row['year']][$row['month_numbers']]['specials'][$row['specialID']]['specialname'] = $row['specialname'];
$data[$row['year']][$row['month_numbers']]['specials'][$row['specialID']]['date_start'] = $row['date_start'];
$data[$row['year']][$row['month_numbers']]['specials'][$row['specialID']]['date_end'] = $row['date_end'];
$data[$row['year']][$row['month_numbers']]['specials'][$row['specialID']]['amount'] = $row['amount'];
$data[$row['year']][$row['month_numbers']]['specials'][$row['specialID']]['adjustment_type'] = $row['adjustment_type'];
endif;
You end up building an array that looks like this. (Not showing YEAR in this image)
Now instead of having a single WHILE statement within the display, we use several foreach statements to loop through this data.
Within the monthly foreach loop you check for the âspecialsâ key and if found, loop through these âSpecialsâ records to build for example a tooltip display.
$specials='';
//check for specials and build tooltip
if(array_key_exists('specials', $row)):
$plural = (count($row['specials']) > 1 ? 's' : '');
$specials .= '<div class="tooltip">Special'.$plural;
$specials .= '<span class="tooltiptext">'."\r";
foreach($row['specials'] as $s):
//format special display
$specials .= '<h3>'.$s['specialname'].'</h3><br />'."\r";
$specials .= $s['date_start'].' <i>through</i> '.$s['date_end'].'<br />'."\r";
$specials .= '<span style="float:left;"><i>Amount:</i> £'.$s['amount'].'</span>'."\r";
$specials .= '<span style="float:right;"><i>Type:</i> '.$s['adjustment_type'].'</span><br /><br />'."\r";
endforeach;
$specials .= '</span>'."\r";
$specials .= '</div>'."\r";
endif;
By hovering over the tooltip link for a special, the details of the special can be seen.
Note that I added another special record to my DB for âsummer monthsâ and so the tooltip link is now plural (with âSâ) and 2 records are shown in the popup.
Hereâs is my complete sample.
<?php
$data = array();
$sql = "SELECT
p.id
, p.year
, p.month_numbers
, CONCAT(p.year, '-', LPAD(CONVERT(p.month_numbers, UNSIGNED INTEGER), 2, '0')) AS yearmonth
, CASE p.name when 'MID WEEK' THEN p.price ELSE 0 END midweek
, CASE p.name when 'FRIDAY' THEN p.price ELSE 0 END friday
, CASE p.name when 'SATURDAY' THEN p.price ELSE 0 END saturday
, CASE p.name when 'SUNDAY' THEN p.price ELSE 0 END sunday
, s.id AS specialID
, s.name AS specialname
, s.date_start
, s.date_end
, s.amount
, s.adjustment_type
FROM `standard_prices` AS p
LEFT JOIN `special_prices` AS s
ON CONCAT(p.year, '-', LPAD(CONVERT(p.month_numbers, UNSIGNED INTEGER), 2, '0')) BETWEEN DATE_FORMAT(s.date_start,'%Y-%m') AND DATE_FORMAT(s.date_end,'%Y-%m')
ORDER BY p.year, CONVERT(p.month_numbers, UNSIGNED INTEGER)";
$result = $con->query($sql);
while ($row = $result->fetch_assoc()){
$data[$row['year']][$row['month_numbers']]['week']['midweek'][$row['id']] = $row['midweek'];
$data[$row['year']][$row['month_numbers']]['week']['friday'][$row['id']] = $row['friday'];
$data[$row['year']][$row['month_numbers']]['week']['saturday'][$row['id']] = $row['saturday'];
$data[$row['year']][$row['month_numbers']]['week']['sunday'][$row['id']] = $row['sunday'];
if(!empty($row['specialID'])):
$data[$row['year']][$row['month_numbers']]['specials'][$row['specialID']]['specialname'] = $row['specialname'];
$data[$row['year']][$row['month_numbers']]['specials'][$row['specialID']]['date_start'] = $row['date_start'];
$data[$row['year']][$row['month_numbers']]['specials'][$row['specialID']]['date_end'] = $row['date_end'];
$data[$row['year']][$row['month_numbers']]['specials'][$row['specialID']]['amount'] = $row['amount'];
$data[$row['year']][$row['month_numbers']]['specials'][$row['specialID']]['adjustment_type'] = $row['adjustment_type'];
endif;
}
/*
echo "<pre>";
print_r($data);
echo "<pre>";
*/
?>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Standard Prices</title>
<style type="text/css">
.pricelist{
margin:50px auto;
background-color:#FFFFFF;
font-size:18px;
font-family:'Segoe UI', Arial;
border-collapse: collapse;
border:1px solid #C6C6C6;
}
.pricelist td{
padding:2px 9px;
border:1px solid #D4D4D4;
}
.pricelist td.title{
text-align:center;
font-size:32px;
font-weight:550;
border:1px solid #D4D4D4;
}
.pricelist td.heading{
text-align:center;
font-size:20px;
font-weight:550;
border:1px solid #D4D4D4;
}
.pricelist td.monthhead{
text-align:left;
font-size:20px;
font-weight:550;
border:1px solid #D4D4D4;
}
.right{
text-align:right;
}
.tooltip {
position: relative;
display: inline-block;
cursor: pointer;
}
.tooltip .tooltiptext {
visibility: hidden;
width: 400px;
background-color:#fff;
color: #000;
text-align: center;
padding: 8px;
border-radius: 8px 0 8px 8px;
border:3px solid #009933;
position: absolute;
z-index: 1;
}
.tooltip:hover .tooltiptext {
visibility: visible;
}
.tooltiptext {
display: block;
margin-top: -.5em;
margin-left: -428px;
}
.tooltiptext h3 {
font-size: 18px;
display: inline-block;
text-align: center;
line-height:1em;
padding:10px 0;
margin:0;
}
.tooltiptext i {
font-weight:lighter;
}
</style>
</head>
<body>
<table border=0 class="pricelist">
<?php
$years = array();
$months = ['ImNotAMonth','January','February','March','April','May','June','July','August','September','October','November','December'];
foreach($data as $year => $arr):
if(!in_array($year,$years)):
$years[] = $year;
echo '<tr>
<td colspan="6" class="title">Prices '.$year.'</td>
</tr>
<tr>
<td style="width:16.666%;" class="heading"> </td>
<td style="width:16.666%;" class="heading">Midweek</td>
<td style="width:16.666%;" class="heading">Friday</td>
<td style="width:16.666%;" class="heading">Saturday</td>
<td style="width:16.666%;" class="heading">Sunday</td>
<td style="width:16.666%;" class="heading">Specials</td>
</tr>'."\r";
endif;
foreach($data[$year] as $month_numbers => $row):
$specials='';
//check for specials and build tooltip
if(array_key_exists('specials', $row)):
$plural = (count($row['specials']) > 1 ? 's' : '');
$specials .= '<div class="tooltip">Special'.$plural;
$specials .= '<span class="tooltiptext">'."\r";
foreach($row['specials'] as $s):
//format special display
$specials .= '<h3>'.$s['specialname'].'</h3><br />'."\r";
$specials .= $s['date_start'].' <i>through</i> '.$s['date_end'].'<br />'."\r";
$specials .= '<span style="float:left;"><i>Amount:</i> £'.$s['amount'].'</span>'."\r";
$specials .= '<span style="float:right;"><i>Type:</i> '.$s['adjustment_type'].'</span><br /><br />'."\r";
endforeach;
$specials .= '</span>'."\r";
$specials .= '</div>'."\r";
endif;
echo '<tr>
<td class="monthhead">'.implode(", ",array_map(function ($a) use ($months) { return $months[(int) $a]; },explode(",",$month_numbers))).'</td>
<td class="right">£ '.max($row['week']['midweek']).'</td>
<td class="right">£ '.max($row['week']['friday']).'</td>
<td class="right">£ '.max($row['week']['saturday']).'</td>
<td class="right">£ '.max($row['week']['sunday']).'</td>
<td>'.$specials.'</td>
</tr>'."\r";
endforeach;
endforeach;
?>
</table>
</body>
</html>
Sorry for the long post but wanted to explain things. Also the suggestion of making a separate query for specials and doing checks within primary result loop is also viable. As the data array comes very natural to me for dealing with difficult result sets I tend to use it. Also note I most certainly would avoid a nested query if at all possible though some might see that as a solution.