Json encode in the Wrong Format - php

I am trying to make some statistics over visits on my site. I know that I could use Google Analytics(which I do), but I want to try to do something myself and it is a good way to learn it.
Problem:
I select dates in my database and sort them to fit this week. After that, I want to add them to a json file. That json file are used by CanvasJS to make a Chart. I have tried some different ways, just to get it simi-working. But the format of the json array, is not the one CanvasJS want.
What I need:
{ visits:[[2019-02-12, 49,],[2019-02-13,40,],[2019-02-14,46,],[2019-02-15,37,], [2019-02-16,31,],[2019-02-17,38,],[2019-02-18,4,] }
What I get:
{ "visits":{"2019-02-12":49,"2019-02-13":40,"2019-02-14":46,"2019-02-15":37,"2019-02-16":31,"2019-02-17":38,"2019-02-18":4} }
My PHP Script:
// Get first and last day of the current week
$first_day = date('Y-m-d', strtotime("- 6 days"));
$last_day = date('Y-m-d', strtotime("+ 1 days "));
// Return all results the past 7 days
$sql = "SELECT date FROM table WHERE date >= '" . $first_day . "' AND date < '" . $last_day . "'";
if($result = $conn->query($sql)){
$response = array();
$visits = array();
while($row = $result->fetch_array(MYSQLI_ASSOC)){
$old_date = $row['date'];
$old_date_timestamp = strtotime($old_date);
$new_date = date('Y-m-d', $old_date_timestamp);
// I don't need the keys, but I cant avoid it to
// get it to work....
$visits[] = array(
'date' => $new_date
);
}
// Add sum of Dates
$response['visits'] = array_count_values(array_column($visits, 'date'));
// Save to json File
$fp = fopen('results.json', 'w');
fwrite($fp, json_encode($response));
fclose($fp);
}
$conn->close();
Thanks to anyone able to help.

Ignoring any quote-related issues you might think are problems (but aren't), it seems your main difference is between what you want...
[[2019-02-12, 49,],...
and what you have...
{"2019-02-12":49,...
This is because array_count_values() creates an associative array, with your dates as keys.
Your issue could be greatly simplified by getting your database to group and count instead of doing it in PHP. You can also benefit from using a prepared statement instead of direct value injection.
// Get first and last day of the current week
$first_day = date('Y-m-d', strtotime("- 6 days"));
$last_day = date('Y-m-d', strtotime("+ 1 days "));
$sql = <<<_SQL
SELECT DATE(`date`), COUNT(1)
FROM `table` WHERE `date` BETWEEN ? AND ?
GROUP BY DATE(`date`)
_SQL;
$stmt = $conn->prepare($sql);
$stmt->bind_param('ss', $first_day, $last_day);
$stmt->execute();
$stmt->bind_result($date, $count);
while ($stmt->fetch()) {
$visits[] = [$date, $count];
}
$response = [ 'visits' => $visits ];
// Save to json File
$fp = fopen('results.json', 'w');
fwrite($fp, json_encode($response));
fclose($fp);

What I understand is you want to group same date as array inside visits, if I understand correctly here is a solution.
Add one for more loop to make format php array before change it to json string.
$first_day = date('Y-m-d', strtotime('- 6 days'));
$last_day = date('Y-m-d', strtotime('+ 1 days '));
$sql = "SELECT date FROM table WHERE date >= '" . $first_day . "' AND date < '" . $last_day . "'";
if ($result = $conn->query($sql)) {
$response = [];
$visits = [];
while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
$old_date = $row['date'];
$old_date_timestamp = strtotime($old_date);
$new_date = date('Y-m-d', $old_date_timestamp);
$visits[] = [
'date' => $new_date
];
}
// here the change start
$format = [];
foreach ($visits as $visit) {
$format[$visit['date']][] = $visit['date'];
}
$response['visits'] = array_values($format);
// here the change end
$fp = fopen('results.json', 'w');
fwrite($fp, json_encode($response));
fclose($fp);
}
$conn->close();
if you don't need the key date here another solution
$first_day = date('Y-m-d', strtotime('- 6 days'));
$last_day = date('Y-m-d', strtotime('+ 1 days '));
$sql = "SELECT date FROM table WHERE date >= '" . $first_day . "' AND date < '" . $last_day . "'";
if ($result = $conn->query($sql)) {
$response = [];
$visits = [];
while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
$old_date = $row['date'];
$old_date_timestamp = strtotime($old_date);
$new_date = date('Y-m-d', $old_date_timestamp);
$visits[$new_date][] = $new_date; // change here
}
$response['visits'] = array_values($visits); // change here
$fp = fopen('results.json', 'w');
fwrite($fp, json_encode($response));
fclose($fp);
}
$conn->close();
Explanation
in PHP there are two type of array indexed and associative, when you change PHP array to json string, indexed array goes to be array and associative goes to be object.
example
$indexed = [
0 => 'foo',
1 => 'bar'
];
$associative = [
'one' => 'foo',
'two' => 'bar'
];
var_dump(json_encode($indexed));
// [
// "foo",
// "bar"
// ]
var_dump(json_encode($associative));
// {
// one: "foo",
// two: "bar"
// }
in my code I use visit date as a key, that way the same date will goes into same array, and I array_values to convert associative to indexed

Related

Inputting/overwriting information in arrays with PHP

I have a small PHP page which takes data from MySQL and displays it via PHP in a monthly calendar. I'm having trouble arranging the data properly within an array to get the desired output.
First, I will describe what I would like to happen:
students come to classes on regular days of the week
they can also make or cancel reservations
the calendar also displays days when the school is not open
In order to display this data on the calendar, I use MySQL to output data from a variety of sources, and then input that into an array with PHP, which I sort by date and output.
My issue is, I would like to be able to handle more than one row of data per day, but because I am using the date as the key, I am limited on only displaying one result per day. If I use a loop to append the date with a counter in the key, I get overlapping results in situations where someone made a reservation and then cancelled that reservation on the same day.
As for my code...
First, I check to see if the student is registered in a weekly class, then input that class into the array.
$sql = "SELECT StudentDB.studentid, ClassDB.classID, ClassDB.class_level, ClassDB.class_title, ClassDB.time, ClassDB.teacher, StudentDB.first_name, StudentDB.last_name, StudentDB.payment_amount, ClassDB.day
FROM ClassDB
INNER JOIN RegDB ON ClassDB.classID = RegDB.classid
INNER JOIN StudentDB ON StudentDB.studentID = RegDB.studentid
WHERE StudentDB.studentid = '$studentid'";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) { // DISPLAY REGULAR CLASS DATA
$dayofclass = $row['day'];
$class_level = $row['class_level'];
$class_title = $row["class_title"];
$day = $row["day"];
$class_time = $row["class_time"];
$time = $row["time"];
// check which dates match the days of the week and store in an array
for ($i=1;$i<=$n;$i++){
if ($i<10) {
$i = "0" . $i;
}
$day=date("l",strtotime($yearmonth.$i)); //find weekdays
if($day==$dayofclass){
$time = date("H:i",strtotime($row['time']));
$dates[]=$yearmonth.$i;
$datesdata[$yearmonth.$i] = "0";
$timedata[$yearmonth.$i] = $time;
$classiddate[$yearmonth.$i] = $row['classID'];
}
}
}
echo "</table>";
$conn->close();
}
After that, I check for specific reservations (cancelations, irregular reservations, waitlists) and input them into the array:
$lowerlimit = $yearmonth . "01";
$upperlimit = $yearmonth . "31";
$sql = "SELECT AttendanceDB.*, ClassDB.*
FROM StudentDB
INNER JOIN AttendanceDB ON StudentDB.studentid = AttendanceDB.studentid
INNER JOIN ClassDB ON AttendanceDB.classid = ClassDB.classID
WHERE StudentDB.studentid = '$studentid'
AND AttendanceDB.class_time >= '$lowerlimit'
AND AttendanceDB.class_time <= '$upperlimit'";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
$loopcount = 0;
// store furikae data in the array
while($row = $result->fetch_assoc()) {
$phpdate = strtotime( $row["class_time"] );
$time = date("H:i",strtotime($row['time']));
$mysqldate = date( 'Y-m-d', $phpdate );
$loopcount++;
$mysqldate = $mysqldate . "+" . $loopcount;
// $loopcount++;
// $mysqldate = $mysqldate . "+" . $loopcount;
$previousdate = $mysqldate;
$previousfurikae = $row['furikae'];
if ($row["furikae"] == 3){
$dates[]=$mysqldate;
$datesdata[$mysqldate] = "1";
$timedata[$mysqldate] = $time;
$classiddate[$mysqldate] = $row['classID'];
} elseif ($row["furikae"] == 8 OR $row["furikae"] == 7) {
$dates[]=$mysqldate;
$datesdata[$mysqldate] = "3";
$timedata[$mysqldate] = $time;
} elseif ($row["furikae"] == 2) {
$dates[]=$mysqldate;
$datesdata[$mysqldate] = "2";
$timedata[$mysqldate] = $time;
}
}
}
$conn->close();
Then finally I check the school calendar and input the days off into the array:
$sql = "SELECT *
FROM SchoolScheduleDB
WHERE date >= '$lowerlimit'
AND date <= '$upperlimit'";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
// store furikae data in the array
while($row = $result->fetch_assoc()) {
$phpdate = strtotime( $row["date"] );
// $time = date("H:i",strtotime($row['time']));
// $mysqldate = date( 'Y-m-d', $phpdate ) . " " . $time;
$mysqldate = date( 'Y-m-d', $phpdate );
$dates[]=$mysqldate;
$datesdata[$mysqldate] = "666";
}
}
$conn->close();
The way I intended it to work was that:
First the regular classes would be input
Then any reservations would overwrite the original plans
And finally the school calendar would overwrite everything
Currently, this functions as it should, but it is limited to displaying 1 result per day, but I would like to be able to display more than 1 result per day for students who come to multiple classes.
Thank you for your help. If I made any mistakes in my question or my question is unclear I will do my best to revise it.
You can make a Sub-Array for each date by using edged brackets:
$data[20180528][] = 'aa';
$data[20180528][] = 'bb';
$data[20180529][] = 'cc';
$data[20180529][] = 'dd';
$data[20180529][] = 'ee';
will give you an Array like this:
20180528 => aa
=> bb
20180529 => cc
=> dd
=> ee

Get only time from a string of more than one dateTime

I have a string which gets the dateTime as a GROUP from the database, so I get something like this
2017-10-20 05:00:00,2017-10-20 09:00:00,2017-10-20 07:00:00,2017-10-20 13:30:00,2017-10-20 16:00:00,2017-10-20 13:00:00,2017-10-20 06:00:00,2017-10-20 09:30:00,2017-10-20 10:30:00,2017-10-20 15:30:00,2017-10-20 17:00:00
Note that all of the dates are the same, i.e., 2017-10-20. Since I don't need to worry about the different dates. All I want is to simply extract the time from this string, and print it nicely like this
05:00, 09:00, 07:00, 13:30, 16:00 //and so on
I don't want the seconds, it would be unnecessary.
I tried using PHP's strtotime() function but it fails and gives 00:00:00 instead.
Any ideas? I am using Laravel as a templating engine.
Do it within your SQL statement.
SELECT TIME_FORMAT(`dateColumn`, '%H:%i') FROM ...
Step by step:
$dateTimes = '2017-10-20 05:00:00,2017-10-20 09:00:00,2017-10-20 07:00:00,2017-10-20 13:30:00,2017-10-20 16:00:00,2017-10-20 13:00:00,2017-10-20 06:00:00,2017-10-20 09:30:00,2017-10-20 10:30:00,2017-10-20 15:30:00,2017-10-20 17:00:00';
$dateTimesArray = explode(',', $dateTimes);
$timesArray = [];
foreach($dateTimesArray as $dateTime) {
$timesArray[] = date('H:i', strtotime($dateTime));
}
$times = join(',', $timesArray);
<?php
$tGroup = "2017-10-20 05:00:00,2017-10-20 09:00:00,2017-10-20 07:00:00,2017-10-20 13:30:00,2017-10-20 16:00:00,2017-10-20 13:00:00,2017-10-20 06:00:00,2017-10-20 09:30:00,2017-10-20 10:30:00,2017-10-20 15:30:00,2017-10-20 17:00:00";
$retVal = "";
$dates = explode(",", $tGroup);
foreach ($dates as $date) {
$time = date('H:i', strtotime($date));
// use this if you want to print AM/PM format
//$time = date('h:i A', strtotime($date));
$retVal .= $time . ", ";
}
echo trim($retVal," ,");

Increment date when it should be a new month

I have a PHP script which records things based on the day. So it will have a weekly set of inputs you would enter.
I get the data correctly, but when i do $day ++; it will increment the day, going passed the end of the month without ticking the month.
example:
//12/29
//12/30
//12/31
//12/32
//12/33
Where it should look like
//12/29
//12/30
//12/31
//01/01
//01/02
My script is as follows:
$week = date ("Y-m-d", strtotime("last sunday"));
$day = $week;
$run = array(7); //this is actually defined in the data posted to the script, which is pretty much just getting the value of the array index for the query string.
foreach( $run as $key=>$value)
{
$num = $key + 1;
$items[] = "($num, $user, $value, 'run', '$day')";
echo "".$day;
$day ++;
}
Should I be manipulating the datetime differently for day incrementations?
You can use
$day = date("Y-m-d", strtotime($day . " +1 day"));
instead of
$day++;
See live demo in ideone
You refer to $day as a "datetime" but it is just a string - that is what date() returns. So when you do $day++ you are adding 1 to "2015-12-02". PHP will do everything it can to make "2015-12-02" into a number and then add 1 to it, which is not date math. Here is a simple example:
<?php
$name = "Fallenreaper1";
$name++;
echo $name
?>
This will output:
Fallenreaper2
This is how I would do it, using an appropriate data type (DateTime):
<?php
$day = new DateTime('last sunday');
$run = array(7);
foreach ($run as $key => $value) {
$num = $key + 1;
$dayStr = $day->format('Y-m-d');
$items[] = "($num, $user, $value, 'run', '$dayStr')";
echo $dayStr;
$day->modify('+1 day');
}
To increase time you should use strtotime("+1 day");
here is simple example of using it
<?php
$now_time = time();
for($i=1;$i<8;$i++) {
$now_time = strtotime("+1 day", $now_time);
echo date("Y-m-d", $now_time) . "<br>";
}
?>

Print out custom name of the month in php

I can't figure it out! I've created an array of months in "Slovenian language" and now, I would want to display my month's name instead of the number. Instead of working, it writes out - 32014vEurope/Berlin11bEurope/BerlinWed and some more weird stuff, it should obviously print out November in my case. I would like to solve this problem with arrays, but It just wouldn0t convert the number of 'n' to the requested month.
function kliknjena($link, $mojster)
{
$meseci[1] = "Januar";
$meseci[2] = "Februar";
$meseci[3] = "Marec";
$meseci[4] = "April";
$meseci[5] = "Maj";
$meseci[6] = "Junij";
$meseci[7] = "Julij";
$meseci[8] = "Avgust";
$meseci[9] = "September";
$meseci[10] = "Oktober";
$meseci[11] = "November";
$meseci[12] = "December";
$sql = "SELECT naslov, podnaslov, vsebina, ustvarjeno, slug FROM novica
where slug = '$mojster'
limit 1";
$result = mysqli_query($link, $sql);
if (mysqli_num_rows($result) > 0)
{
while($row = mysqli_fetch_assoc($result))
{
echo "<h1>".$row["naslov"]."</h1>";
$timestamp = strtotime($row["ustvarjeno"]);
$m = date("n", $timestamp);
echo date("d. $meseci[$m]; Y, H:i:s", $timestamp);
echo "<p>".$row["podnaslov"]."</p>"."<br>";
echo "<p>".$row["vsebina"]."</p>"."<br>";
}
}
else
{
echo "0 results";
}
}
Use:
echo date('d. ', $timestamp) . $meseci[$m] . date('; Y, H:i:s', $timestamp);
What's happening is that the month name is being substituted into the date() argument, and then all the letters in the month are being treated as formatting characters, so they get replaced with the corresponding fields from the date and time.

Table starting from present date

$sqlStr = "SELECT name, datescheduled
FROM table
WHERE datescheduled > NOW()
ORDER BY datescheduled DESC";
I would like to echo a table with the results above. I would like a row for each day for the next 90 days regardless of whether or not the MySQL table has an entry for that day. How can I do this?
Add:
$query1 = mysql_query($sqlStr);
echo "<table><tr>";
echo "<th>Name</th><th>Date Scheduled</th>";
while ($rows = mysql_fetch_assoc($query1)) {
echo "<td>" .$rows['name']. "</td>";
echo "<td>" .$rows['datescheduled']. "</td>";
}
echo "</tr><table>";
//select rows for next 90 days and read them into $rows
//use datescheduled as the key
//assumes there will only be 1 row per date and datescheduled is in Y-m-d format
$sqlStr = "SELECT name, datescheduled
FROM table
WHERE datescheduled > NOW()
AND datescheduled < date_add(now(),INTERVAL 90 DAY)
ORDER BY datescheduled DESC";
$rs = mysql_query($sqlStr);
$rows = array();
while($r = mysql_fetch_assoc($rs)) {
$rows[$r['datescheduled']] = $r;
}
//add missing dates to $rows with name = false
$begin = new DateTime();
$end = new DateTime();
$end->modify('+90 day');
$interval = new DateInterval('P1D');
$period = new DatePeriod($begin, $interval, $end);
//iterate through the next 90 days
foreach ($period as $dt) {
$date_key = $dt->format( "Y-m-d" );
if(!isset($rows[$date_key])) {
//table doesn't contain a row for this date, so add it
$rows[$date_key] = array('datescheduled' => $date_key, 'name' => false);
}
}
//do something with $rows
So you can use this basic setup. I assume based on your wording that there would only be one entry per row, but it would be easy to adjust accordingly.
//Get the current date
$date = date('Y-m-d');
//Set the table header
$str = '<table><thead><tr><th>Name</th><th>Date</th></tr></thead><tbody>';
//START THE WHILE LOOP GETTING THE FETCH ASSOC
//Go through for 90 days
for ( $i = 0; $i < 90; $i++ ) {
$str .= '<tr>';
if ( $row['date'] == date('Y-m-d', strtotime('-'.$i.' day', $date)) {
$str .= '<td>'.$row['name'].'</td><td>'.$row['date'].'</td>';
} else {
$str .= '<td></td><td></td>';
}
$str .= '</tr>';
}
//END WHILE LOOP NOT INCLUDED
$str .= '</table>';
echo $str;

Categories