I have a database table called payments which contains date,amount fields. I want to take values from amount field and SUM up all amounts by date and take the results to html table then output them like on the image.
I have created dates dynamically so that they will be equal nomatter which months example January its 1-31 and February its 1-31. Where there is a weekend or the date is invalid i want the value to be zero. What i want is like this table [table][1] [1]: http://i.stack.imgur.com/iMOl3.jpg
This is what i am getting [output][1][1]: http://i.stack.imgur.com/MJpyT.jpg
******NOTE***** I THINK MY SOLUTION IS NOT THE BEST SOLUTION TO MY PROBLEM. IF POSSIBLE JUST TAKE I VIEW ON THE PICTURE WHICH I WANT AND FIND ME THE BEST SOLUTION. I WANT TO BE HELPED IN EITHER STEPS TO ACHIEVE IT OR A SOLUTION
I know that i am using a depricated mysql synthax please ignore that and help on my problem.
<table border="1" align="center">
<?php
session_start();
include("connection/db_con.php");
$sym='-';
$d=array();
///Insert values of month for period selected into an array
$a = $_POST['dat'];
$b = $_POST['dat2'];
$mnth=array();
$m_nam=array();
$m_nm=array();
$m_nam[]="Day";
//////New way of getting months in format Y-m
$start = new DateTime($a);
$start->modify('first day of this month');
$end = new DateTime($b);
$end->modify('first day of next month');
$interval = DateInterval::createFromDateString('1 month');
$period = new DatePeriod($start, $interval, $end);
foreach ($period as $dt) {
$mnth[]=$dt->format("Y-m");
$m_nam[]=date('F-Y', strtotime($dt->format("Y-m")));
$m_nm[]=date('M', strtotime($dt->format("Y-m")));
}
///////End of New way
echo "<tr bgcolor='#999999'>";
foreach ($m_nam as $m)
{
echo"<td>".$m."</td>";
}
echo"</tr>";
/////////End insert////////////////////////
$day=0;
for($x=1; $x<=31; $x++)
{
$day=$day+1;
echo"<tr>";
echo"<td>".$day."</td>";
$d=$sym.$x;
foreach($mnth as $mon)
{
$dat=$mon.$d;
$qry=mysql_query("SELECT SUM(amount) AS total_disb FROM payments where dat='$dat'")or die(mysql_error());
$row=mysql_fetch_assoc($qry);
$sum = $row['total_disb']+0;
echo"<td>".$sum."</td>";
}
echo"</tr>";
}
?>
</table>
Here's a rewrite of the code you provided, it's using dummy random data instead of DB and there is no logic for POST variables, but that you can replace with your code.
<?php
// session start, db connection goes here
echo '<table border="1" align="center">';
// Example of post vars
$start = new DateTime('2015-11-10');
$end = new DateTime('2016-02-28');
// Note: not sure why OP used modify here
$interval = new DateInterval('P1M');
$daterange = new DatePeriod($start, $interval, $end);
// Table Header row 1
echo '<tr><th>Day</th>';
foreach ($daterange as $date) {
echo '<th colspan="2">'.$date->format("F Y").'</th>';
}
echo '</tr>';
// Temporary month store
$months = array();
// Table Header row 2
echo '<tr style="background-color:#22bb22;"><th></th>';
foreach ($daterange as $date) {
$months[] = $date->format("F");
echo '<th>Daily</th>';
echo '<th>Cumulative</th>';
}
echo '</tr>';
// Table Body
$sumc = array();
for ($d = 1; $d <= 31; $d++) {
echo '<tr><td>'.$d.'</td>';
foreach ($months as $month) {
$db_date = $month.'-'.$d; // used for db query
// dummmy data (replace with db query result)
$sum = mt_rand(0, 999);
echo '<td>'.$sum.'</td>';
if(!array_key_exists($month, $sumc)) {
$sumc[$month] = 0;
}
$sumc[$month] = (int)$sum + $sumc[$month];
echo '<td>'.$sumc[$month].'</td>';
}
echo '</tr>';
}
echo '</table>';
?>
Also the condition:
Where there is a weekend or the date is invalid i want the value to be
zero.
is it correct to assume that these are taken care because the DB query would return 0? Or do you have to check in the code for weekends even if the DB query returns an amount from total_disb that is >0?
You have just messed up a little with dates, if I'm understanding where your problem is you can do it directly from SQL, try something like:
SELECT SUM(amount) AS total_disb , MONTH(DateTime) , DAY(DateTime)
FROM payments
GROUP BY DATE(DateTime), DAY(DateTime)
Change DateTime for your variable or column names.
If you also want to SUM the months totals at the end of the table I'd recommend you to make a new query like:
SELECT SUM(amount) AS total_disb , MONTH(DateTime)
FROM payments
GROUP BY DATE(DateTime), MONTH(DateTime)
Another option would be to increment a variable while you loop to print the values, but in my opinion a new query is more simple.
If this is not what you need leave a comment and I will edit it.
To get to the cumulative you have to store the previous sum result and add it to the current iteration in the loop, something like:
$sumc = array();
foreach($mnth as $mon) {
$dat = $mon . $d;
$qry = mysql_query("SELECT SUM(amount) AS total_disb FROM payments where dat='$dat'") or die(mysql_error());
$row = mysql_fetch_assoc($qry);
$sum = $row['total_disb'];
if(isset($sumc[$mon])) {
$sumc[$mon] = (int)$sum + $sumc[$mon];
} else {
$sumc[$mon] = (int)$sum;
}
echo "<td>" . $sum . "</td>";
echo "<td>" . $sumc[$mon] . "</td>";
}
should probably work.
(Note: that you are missing a second row for 'daily' and 'cumulative' and once you have that row you need to use colspan to span the columns of the Months across). See an example of colspan here.
Related
I want to print a table of this week with the tasks you have to do each day, but for a certain reason the first value of the task column is printed above the table and the second value is shown in the first task cell. For this reason, the last cell of the last column is always empty. How do I fix this?
$weekdagen = ["maandag","dinsdag","woensdag","donderdag","vrijdag","zaterdag","zondag"];
//today
$vandaag = date("d-m-Y",time());
//day of week starting on Monday (-1)
$weekdag = date('w', strtotime($vandaag)) -1;
$i = 0;
//get first day of this week
$startWeek = date('Y-m-d', strtotime('-'.($weekdag).' days'));
$datum = $startWeek;
while($i<7)
{
print '<tr>
<td>'.$weekdagen[$i+1].'</td>
<td>'.$datum.'</td>
<td>' .printTasks($datum); '</td>
</tr>';
$i++;
$datum = date('Y-m-d', strtotime($datum.' +1 days'));
}
function printTasks($date) {
$sql = "SELECT taa_omschr FROM taken where taa_datum = '".$date."'";
$res = SQLexec($sql);
while($row = $res->fetch_assoc()) { print $row[taa_omschr] . '<br>'; }
}
I changed my SQL statement to $date - 1 day so the right task is shown next to the right date, but it doesn't solve the problem that the last cell is empty.
I have this query now:
SELECT DATE_FORMAT(`dataNl`, \'%Y%m\') AS `Ym`, COUNT(*) AS `totale`
FROM `noleggio`
GROUP BY `Ym`
This help to get data for each month, but if a month with 0 value, this doesn't exist in the database, so I can't get it. I need a query that add remaining month setting the COUNT field to 0.
I made a PHP code to add months with 0 value into the array, but it only works if the year is only one, if I want to get more, this needs a lot of tricky code, I think there could be a solution with SQL.
This is the PHP code:
$t = array();
$m = array();
foreach ($months as $val) {
$t[] = $val['totale'];
$m[] = $val['Ym'];
}
for ($i = 0; $i < 12; ++$i) {
if (in_array($i + 201801, $m) == false) {
array_splice($t, $i, 0, 0);
}
}
Here is a PHP solution which requires min and max dates from the database:
// use the query SELECT MIN(dataNl), MAX(dataNl) FROM ... to
// find the first and last date in your data and use them below
$dates = new DatePeriod(
DateTime::createFromFormat('Y-m-d|', '2018-01-15')->modify('first day of this month'),
new DateInterval('P1M'),
DateTime::createFromFormat('Y-m-d|', '2018-12-15')->modify('first day of next month')
);
// assuming $rows contain the result of the GROUP BY query...
foreach ($dates as $date) {
$datestr = $date->format('Ym');
$index = array_search($datestr, array_column($rows, 'Ym'));
if ($index === false) {
echo $datestr . ' -> 0' . PHP_EOL;
} else {
echo $datestr . ' -> ' . $months[$index]['totale'] . PHP_EOL;
}
}
Try the below query:
SELECT DATE_FORMAT(`dataNl`, \'%Y%m\') AS `Ym`, COUNT(*) AS `totale`
FROM `noleggio`
GROUP BY MONTH(`dataNl`)
Here's my code that display date with for loop.
Topic: Im creating a script to generate payment due(from to start).
$y = 1;
$period = 3;
$start = date('m/15/Y');
echo "<table>";
echo '<thead><th>From</th>';
echo '<th>To</th></thead>';
for ($y; $y <= $period; $y++) {
$month_mid = date("m/15/Y", strtotime($start));
$month_last = date("m/t/Y", strtotime($start));
echo '<td>'.$month_mid = date("m/t/Y", strtotime($start)).'</td>';
echo '<td>'.$month_last = date("m/15/Y", strtotime($start)).'</td></tr>';
$start = date("m/d/Y",strtotime($start." +1month"));
}
echo '</table>';
output I get:
09/15/2017 09/30/2017
10/15/2017 10/31/2017
11/15/2017 11/31/2017
I want to appear like this:
09/15/2017 09/30/2017
09/30/2017 10/15/2017
10/15/2017 10/31/2017
Im new in date php hope you can help me with this thanks.
Here you go:
$y = 1;
$period = 5;
$start = date('m/15/Y');
echo "<table>";
echo '<thead><th>From</th>';
echo '<th>To</th></thead>';
for ($y; $y <= $period; $y++) {
$month_mid = date("m/15/Y", strtotime($start));
$month_last = date("m/t/Y", strtotime($start));
echo '<tr><td>'.$month_mid = date("m/t/Y", strtotime($start)).'</td>';
echo '<td>'.$month_last = date("m/15/Y", strtotime($start)).'</td></tr>';
$start = date("m/d/Y",strtotime($start." +1month"));
}
echo "</table>";
You missed the opening and clousure of table and <tr>.
You don't want to be skipping ahead by a month in your loop. Well, not the way you are doing it here.
You should use DatePeriod::getEndDate and DatePeriod::getStartDate, along with DateTime::add to skip by semi-monthly amounts. The idea is that you only get to the next month by letting the datetime API add 15 days to a given start date, and use that to figure out the mid-month and end-month dates.
Keep everything in these date objects until you need to format and print them, and save the second one in the pair as input for the next round of the loop at the end where you just have to calculate the new second value.
I feel like you could write a function that gets the "next" date from any other first or last date of the month to simplify the loop.
(Since this is semi-monthly and not bi-weekly, you can actually just walk the months in your period, hard-coding the 15th for one value and using your end-of-month function for the second. It depends on your requirements and how complicated the API gets.)
Or
For each month in your period, calculated the mid-month (i.e., exactly 15 days from the start of the month) and end-month dates. Save them in a Collection of couplets of some sort.
Write a display routine that takes this Collection and outputs the dates, but saves the previous formatted string made from the second item in the couplet as the first item to be printed (after the first line.)
I actually prefer this one because it separates the presentation from the data abstraction, allowing you freedom to display and format the date how you see fit.
But, at the end of the day (pun not intended, but what a great pun), stop using date strings as input to figure out other dates when you have access to normalized epoch representations. This will only lead to madness.
$y = 1;
$period = 3;
$start = date('m/d/Y');
$end = date('m/t/Y');
echo "<table>";
echo '<thead><th>From</th>';
echo '<th>To</th></thead>';
for ($y; $y <= $period; $y++) {
echo '<tr><td>'.$start.'</td>';
echo '<td>'.$end.'</td></tr>';
$getLast = date('d',strtotime($end));
if($getLast >= 28) {
$start = date("m/t/Y", strtotime($start));
$end = date("m/d/Y", strtotime("+15 day", strtotime($end)));
}else {
$start = date("m/d/Y", strtotime("+15 day", strtotime($start)));
$end = date("m/t/Y",strtotime($end));
}
}
echo "</table>";
Result:
From To
09/15/2017 09/30/2017
09/30/2017 10/15/2017
10/15/2017 10/31/2017
I am trying to write a script to pull dates for the next 7 days and put them into a div for each date :
echo '<div class="dateboxcontainer">';
for($i=0; $i<=6; $i++){
echo '<div class="datebox"><div class="topdate">'.strtoupper(date("D d", mktime(0, 0, 0, 0, date("d")+$i, 0))."\n").
'</div><div class="bottomdate">An appointment for the day</div></div>';
}
echo '</div>';
Im now trying to pull data from my database from two fields 'datedroppingoff' and 'datepickingup', which are formatted like this '2013-07-10 14:29:28'.
Im kind of stuck though as im not sure what query to write to put the appointments for each day into each day div where 'some info' currently sits.
Im guessing it would be something like
Select * FROM jobdetails WHERE datedroppingoff OR datepickingup = WHATEVER DAY IS BEING ECHO'D OUT
but im not quite sure how I can compare the date stored in jobdetails for that row to the date being echo'd out ?.
Edit>>>>>
Thanks for the answers below, iv managed to come up with the following, it echos out the date boxes ok, but doesnt bring in any data, so im not sure if I have the sql part correct ?.
echo '<div class="dateboxcontainer">';
$eventdata = <<<SQL
SELECT *
FROM `jobdetails`
SQL;
if(!$events = $db->query($eventdata)){
die('There was an error running the query [' . $db->error . ']');
}
// read first event
if ($nextEvent = mysql_fetch_assoc($events)) { // here is the first one
extract($nextEvent); // prepare its variables
// use the event date it to control the inner loop
$nextDate = $datedroppingoff;
} else // no events?
$nextDate = 0; // prepare a fake date value
// calculate today date
$currentDate = mktime();
// loop on the dates for the next 7 days
for ($i = 0 ; $i < 7; $i++) {
$currentEvents = "";
// loop to print every event for current day (first one already extracted)
while ($nextDate == date("Y-m-d", $currentDate)) { // next event occurs today
// here prepare the var containing the event description
// BTW, I'd use a list for the events
$currentEvents .= "· $name<br>"; // use every field you need from current DB row
// read next event
if ($nextEvent = mysql_fetch_assoc($events)) { // here is the next one
extract($nextEvent); // prepare its variables
// use the event date it to control the inner loop
$nextDate = $datedroppingoff;
} else // no more events?
$nextDate = 0; // prepare a fake date value
}
echo "
<div class='datebox'>
<div class='topdate'>" . strtoupper(date("D d m Y", $currentDate)) . "</div>
<div class='bottomdate'>$currentEvents</div>
</div>";
$currentDate = strtotime("+1 day", $currentDate);
}
echo '</div>';
Can you use something like this:
SELECT * FROM `jobdetails` WHERE (`datedroppingoff ` > '2013-07-01 00:00:00' AND `datedroppingoff ` '2013-07-02 00:00:00') OR (`datepickingup ` > '2013-07-01 00:00:00' AND `datepickingup ` '2013-07-02 00:00:00');
This will have to be repeated per day (i.e. for each of the 5 days)
To do a nice loop, use an array populated with the next 5 dates as strings. Then do a foreach over the dates and run this query.
If you want all dates from now and for the next 5 days, you could use:
WHERE
`datedroppingoff` BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 5 DAY) OR
`datepickingup ` BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 5 DAY)
Let's see how I'd proceed (by the way, this is my first answer, so I'm a little excited...)
First, some basic assumptions for this ultra-fast script (sorry, not so much time now to test it thoroughly).
your Appointment table has a field with the date of the appointment
your result resource ($events) contains only the rows for the current week
the resulting rows are sorted by the date field, ascending (from oldest to newest)
Try this (I've changed a bit your original code, sorry)
// read first event
if ($nextEvent = mysql_fetch_assoc($events)) { // here is the first one
extract($nextEvent); // prepare its variables
// use the event date it to control the inner loop
$nextDate = $DB_field_with_event_date;
} else // no events?
$nextDate = 0; // prepare a fake date value
// calculate today date
$currentDate = mktime();
// loop on the dates for the next 7 days
for ($i = 0 ; $i < 7; $i++) {
$currentEvents = "";
// loop to print every event for current day (first one already extracted)
while ($nextDate == date("Y-m-d", $currentDate)) { // next event occurs today
// here prepare the var containing the event description
// BTW, I'd use a list for the events
$currentEvents .= "· $your_desc_field<br>"; // use every field you need from current DB row
// read next event
if ($nextEvent = mysql_fetch_assoc($events)) { // here is the next one
extract($nextEvent); // prepare its variables
// use the event date it to control the inner loop
$nextDate = $DB_field_with_event_date;
} else // no more events?
$nextDate = 0; // prepare a fake date value
}
echo "
<div class='datebox'>
<div class='topdate'>" . strtoupper(date("D d m Y", $currentDate)) . "</div>
<div class='bottomdate'>$currentEvents</div>
</div>";
$currentDate = strtotime("+1 day", $currentDate);
}
Tried a couple of times on fake data and it should work. IMHO better to directly alias the date field to get the nextDate var directly from the DB, so avoiding the rows,
$nextDate = $DB_field_with_event_date;
I used this in the end which seems to do the job ! :)
// Date box container
echo '<div class="dateboxcontainer">';
// Loop through and create a date for the next 7 days
$days = new DatePeriod(new DateTime, new DateInterval('P1D'), 7);
foreach ($days as $day) {
echo '<div class="datebox">';
echo '<div class="topdate">';
echo strtoupper($day->format('D d')) . PHP_EOL;
echo '</div>';
// Get the names for each day
$theday = strtoupper($day->format('Y-m-d'));
$sqldate = <<<SQL
SELECT *
FROM `jobdetails`
WHERE datedroppingoff = '$theday' OR datepickingup = '$theday'
SQL;
if(!$resultdate = $db->query($sqldate)){
die('There was an error running the query [' . $db->error . ']');
}
while($rowdate = $resultdate->fetch_assoc()){
echo $rowdate['name'];
}
//
echo '</div>';
}
echo '</div>';
//
Here I access 'date' key values from rows of DB table. And I can echo these values, no problem.
$res = $mysqli->query("SELECT * FROM alfred ORDER BY id ASC");
$row = $res->fetch_all(MYSQLI_ASSOC);
foreach ($row as $key => $value){
$availDate = $value['date'];
echo $availDate.'<br />';
}
This loop above shows all 'date' values from DB, in this case there are 3 dates- "2012-09-25" "2012-09-27" and "2012-09-29".
But then I need to compare each of these 'date' values against values of $date->format('Y-m-d') from the code below and display each date with corresponding "busy" or "available" status into separate <td> of the table. My following version compares only the "last" value of 'date' key - "2012-09-29", but I need to compare each 'date' value from the array above, it means also "2012-09-25" and "2012-09-27". I have tried many versions but still unsuccessful. Any ideas?
$date = new DateTime();
$endDate = new DateTime('+10 day');
for($date->format('Y-m-d'); $date->format('Y-m-d') < $endDate->format('Y-m-d'); $date->modify('+1 day')){
if ($date->format('Y-m-d') == $availDate){
echo '<td>'.$date->format('Y-m-d/D').' busy</td>';
} else {
echo '<td>'.$date->format('Y-m-d/D').' available</td>';
}
}
Here is the result I am getting now:
2012-09-21/Fri available 2012-09-22/Sat available 2012-09-23/Sun available 2012-09-24/Mon available 2012-09-25/Tue available 2012-09-26/Wed available 2012-09-27/Thu available 2012-09-28/Fri available 2012-09-29/Sat busy 2012-09-30/Sun available
But in fact I need to show "busy" status also into <td> of "2012-09-25" and <td> of "2012-09-27" as these also are 'date' values that are existing in $row array. Unfortunately I can not post any images here to show, but I hope my result above gives you the idea.
SOLVED with the help of in_array below:
$aAvailDate = array();
foreach ($row as $key => $value){
$aAvailDate[] = $value['date'];
}
$date = new DateTime();
$endDate = new DateTime('+10 day');
for($date->format('Y-m-d'); $date->format('Y-m-d') < $endDate->format('Y-m-d'); $date->modify('+1 day')){
if (in_array($date->format('Y-m-d'), $aAvailDate)){
echo '<td>'.$date->format('Y-m-d/D').' busy</td>';
} else {
echo '<td>'.$date->format('Y-m-d/D').' available</td>';
}
}
I haven't tested your code, but I think you are running ->format('Y-m-d') unnecessarily here, and this is messing up your logic.
Every time you run that, PHP is turning your object into a string, which you are then comparing against other strings. This won't do anything useful.
Instead, you should be using the features of the DateTime class to compare the objects themselves. The only time you should need to use the format() method is when outputting to the browser, into an SQL query, etc
Although your Question is Unclear but AFAIK
you want to display "Busy" if available date occurs between given date upto 3 Weeks
otherwise display "free"
I would like to suggest you to do this with MySQL (Not tested)
SELECT *,
IF( `DateCol` BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 3 WEEK), 'Busy','Free')
AS status
FROM TableName
Try a while loop instead of a foreach. Also, compare the DateTime objects directly, not the formatted strings.
$date = new DateTime();
$endDate = new DateTime('+3 week');
while( $date < $endDate) {
if ($date->format('Y-m-d') == $availDate){
echo '<td class="busy">busy</td>';
} else {
echo '<td>free</td>';
}
$date->modify("+1 day");
}
Something like this? (If I understand what you're trying to do correctly)
<?php
$avail_dates = array();
$res = $mysqli->query("SELECT DATE_FORMAT(date, '%Y-%m-%d') AS availDate FROM alfred ORDER BY id ASC");
$row = $res->fetch_all(MYSQLI_ASSOC);
foreach ($row as $key => $value){
$avail_dates[] = $value['availDate'];
}
$startDate = date('Y-m-d');
$endDate = date('Y-m-d', strtotime(date("Y-m-d", strtotime($startDate)) . " +3 week"));
?>
<table>
<?php
foreach ($avail_dates as $availDate){
echo "<tr><td>$availDate</td>";
if (($startDate <= $availDate) && ($endDate >= $availDate)){
echo "<td class='busy'>busy</td>";
}else{
echo "<td>free</td>";
}
echo "</tr>";
}
?>
Instead of printing values, I would add them to the array, and then run a loop on that array, comparing the values to the given start and end dates. I also wouldn't fetch all from the table if you nly need a date.
May be like this?
$date = new DateTime();
$endDate = new DateTime('+3 week');
for($date->format('Y-m-d'); $date->format('Y-m-d') < $endDate->format('Y-m-d'); $date->modify('+1 day')){
$tempDate = $date->format('Y-m-d');
if ($tempDate === $availDate){
echo '<td class="busy">busy</td>';
} else {
echo '<td>free</td>';
}
}